
package com.idealighter.game.roomcard.games.mustniuniu.manager;

import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.RoomCardMustNiuNiuFeeDic;
import com.idealighter.game.dictionary.domain.RoomCardMustNiuNiuFeeDomain;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.common.niuniu.NiuNiuCard;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.roomcard.mustniuniu.RoomCardFeeLog;
import com.idealighter.game.log.struct.roomcard.mustniuniu.RoomCardFeeLog.RoomCardFeeLogFeeType;
import com.idealighter.game.log.struct.roomcard.mustniuniu.RoomCardMustNiuNiuWinLoseLog;
import com.idealighter.game.message.core.ResMessage;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.roomcard.common.AbstractRoomCardGameMgr;
import com.idealighter.game.roomcard.games.mustniuniu.MustNiuNiuTrusteeCardsTypeGetter;
import com.idealighter.game.roomcard.games.mustniuniu.bo.BetTypeBo;
import com.idealighter.game.roomcard.games.mustniuniu.bo.TrusteeBo;
import com.idealighter.game.roomcard.games.mustniuniu.bo.TrusteeBo.TrusteeBetOptionBo;
import com.idealighter.game.roomcard.games.mustniuniu.bo.TrusteeBo.TrusteeCallBankerOptionBo;
import com.idealighter.game.roomcard.games.mustniuniu.bo.TrusteeBo.TrusteeExtrBetOptionBo;
import com.idealighter.game.roomcard.games.mustniuniu.constant.MustNiuNiuAdvanceOption;
import com.idealighter.game.roomcard.games.mustniuniu.constant.MustNiuNiuCardType;
import com.idealighter.game.roomcard.games.mustniuniu.constant.MustNiuNiuGame;
import com.idealighter.game.roomcard.games.mustniuniu.constant.MustNiuNiuGameState;
import com.idealighter.game.roomcard.games.mustniuniu.constant.MustNiuNiuStageTimeConstant;
import com.idealighter.game.roomcard.games.mustniuniu.dto.GameBillInfo;
import com.idealighter.game.roomcard.games.mustniuniu.dto.RoundBillInfo;
import com.idealighter.game.roomcard.games.mustniuniu.message.ResCloseRoomCardMsg.CloseType;
import com.idealighter.game.roomcard.games.mustniuniu.message.ResGameInfoMsg;
import com.idealighter.game.roomcard.games.mustniuniu.struct.MustNiuNiuRoom;
import com.idealighter.game.roomcard.games.mustniuniu.struct.MustNiuNiuSeat;
import com.idealighter.game.roomcard.games.mustniuniu.struct.MustNiuNiuTable;
import com.idealighter.game.roomcard.manager.RoomCardGameCfgMgrScript;
import com.idealighter.game.roomcard.manager.RoomCardRoomListMgrScript;
import com.idealighter.game.roomcard.struct.constant.RoomCardBillTypeConstant;
import com.idealighter.game.roomcard.struct.constant.RoomCardPayByConstant;
import com.idealighter.game.roomcard.struct.constant.RoomCardRemoveConstant;
import com.idealighter.game.roomcard.struct.constant.RoomCardStateConstant;
import com.idealighter.game.roomcard.struct.gamecfg.MustNiuNiuRoomCardGameCfg;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.BasicGameInfoPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuAdvanceOptionPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuBankerChipPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuBetOptionPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuCallBankerMultiplePo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuCardMultiplePo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuCardSpecialMultiplePo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuExtrBetOptionPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuRoundPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuSeatPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.MustNiuNiuStartOptionPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.OwnerBeBankerGameInfoCfgPo;
import com.idealighter.game.roomcard.struct.gamgcfg.value.NiuNiuRoomCardGameCfgValue.ShowCardCallBankerGameInfoCfgPo;
import com.idealighter.game.roomcard.struct.roomcfg.MustNiuNiuRoomCardRoomCfg;
import com.idealighter.game.roomcard.struct.roomcfg.value.MustNiuNiuRoomCardRoomCfgValue;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.game.server.db.datadb.dao.RoomCardGroupDao;
import com.idealighter.game.server.db.datadb.dao.RoomCardNiuniuScoreDetailsDao;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardGroupDomain;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardGroupMemberDomain;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardGroupMemberDomainExample;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardNiuniuScoreDetailsDomain;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardScoreDomain;
import com.idealighter.game.server.db.datadb.mapper.RoomCardGroupMapper;
import com.idealighter.game.server.db.datadb.mapper.RoomCardGroupMemberMapper;
import com.idealighter.game.server.db.datadb.mapper.RoomCardRoomListMapper;
import com.idealighter.game.server.db.datadb.mapper.RoomCardScoreMapper;
import com.idealighter.utils.check.CheckUtil;
import com.idealighter.utils.check.EmptyUtil;
import com.idealighter.utils.code.RandCodeUtil;
import com.idealighter.utils.json.JsonUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * 四人牛牛逻辑处理 .
 * 
 * 
 * @date 2016年1月20日 下午4:45:27
 *
 */
@Singleton
public class MustNiuNiuMgr extends AbstractRoomCardGameMgr<//
    MustNiuNiuRoom, //
    MustNiuNiuTable, //
    MustNiuNiuSeat, //
    MustNiuNiuRoomCardRoomCfg, //
    MustNiuNiuRoomCardRoomCfgValue> {

  private static final Logger LOG = LoggerFactory.getLogger(MustNiuNiuMgr.class);
  // 玩家手上的牌数
  public static final int PLAYER_CARDS = 5;
  // 牛牛定时服务器增加的延时，因为与网络延时等原因
  private static final int ADDED_DELAY = 2;
  private static final int ANSER_TIME = 10; // 10s

  // private final EventMgr eventMgr;
  // 游戏线程executor,可以执行普通任务和定时任务
  private final DisruptorExecutor gameExecutor;
  @Inject
  private MustNiuNiuControlScript niuniuControl;
  @Inject
  private PlayerMgr playerMgr;
  @Inject
  private ScheduleMgr scheduleMgr;
  @Inject
  private MustNiuNiuMsgMgr msgMgr;

  @Inject
  private RoomCardGameCfgMgrScript roomCardGameCfgMgrScript;
  @Inject
  private MustNiuNiuMsgMgr mustNiuNiuMsgMgr;
  @Inject
  private RoomCardMustNiuNiuFeeDic roomCardMustNiuNiuFeeDic;
  @Inject
  private RoomCardNiuniuScoreDetailsDao roomCardNiuniuScoreDetailsDao;
  private RoomCardGroupDao groupDao;

  private RoomCardGroupMapper roomCardGroupMapper;
  private RoomCardGroupMemberMapper roomCardGroupMemberMapper;
  private RoomCardScoreMapper roomCardScoreMapper;

  /**
   * 构造函数.
   * 
   * @param eventMgr 事件管理.
   * @param executorMgr 执行管理.
   * @param scheduleMgr 定时执行器
   * @param roomCardRoomListMapper 房卡房间配置
   * @param roomCardRoomListMgrScript 房卡管理
   */
  @Inject
  public MustNiuNiuMgr(EventMgr eventMgr, ExecutorMgr executorMgr, ScheduleMgr scheduleMgr,
      RoomCardRoomListMapper roomCardRoomListMapper,
      RoomCardRoomListMgrScript roomCardRoomListMgrScript,
      RoomCardGroupMapper roomCardGroupMapper,
      RoomCardGroupMemberMapper roomCardGroupMemberMapper,
      RoomCardScoreMapper roomCardScoreMapper, RoomCardGroupDao groupDao) {
    super(Game.MUST_NIUNIU, eventMgr, executorMgr, scheduleMgr, roomCardRoomListMapper,
        roomCardRoomListMgrScript);

    this.roomCardGroupMapper = roomCardGroupMapper;
    this.roomCardGroupMemberMapper = roomCardGroupMemberMapper;
    this.roomCardScoreMapper = roomCardScoreMapper;
    eventMgr.register(this);
    this.gameExecutor = executorMgr.getRoomCardExecutor(Game.MUST_NIUNIU.getRoomCardModuleId());
    this.groupDao = groupDao;

    // 更新群房间数量
    Map<Integer, Integer> groupRoomNumMap = new HashMap<>();
    for (MustNiuNiuRoom room : super.rooms()) {
      Integer groupId = room.getRoomCfg().getGroupId();
      if (groupId != null && groupId > 0) {
        // 房间数量
        int roomNum = groupRoomNumMap.getOrDefault(groupId, 0) + 1;
        // 更新房间数量
        groupRoomNumMap.put(groupId, roomNum);
      }
    }
    // 更新到数据库
    for (Integer groupId : groupRoomNumMap.keySet()) {
      int roomNum = groupRoomNumMap.get(groupId);
      groupDao.updateGroupRoomCardNum(groupId, roomNum);
    }
  }

  /**
   * 订阅玩家退出游戏事件,在gameExecutor中执行,避免线程安全问题 .
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    Player player = event.player;
    if (player != null) {
      // long playerId = player.getId();
      if (player.curRoom instanceof MustNiuNiuRoom) {
        // gameExecutor.execute(() -> {
        // SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(playerId);
        // if (seat != null) {
        // doExitTabl(seat);
        // }
        //
        // // 玩家是否在牛牛房间中
        // if (player.curRoom instanceof SiRenNiuNiuRoom) {
        // // 退出房间
        // exitRoom(player);
        // // 触发退出
        // GameClearExitEvent gameClearExitEvent =
        // new GameClearExitEvent(player, Game.SIREN_NIUNIU.getDesc());
        // eventMgr.post(gameClearExitEvent);
        // }
        // });
      }
    }
  }

  /**
   * @Title findScoreDetail.
   * @Description 得分详情
   * @date 2018年6月14日 下午4:04:31
   * @param player 玩家
   * @param roomId 房卡编号
   * @return .
   */
  public ResMessage findScoreDetail(Player player, Long roomId) {
    List<RoomCardNiuniuScoreDetailsDomain> detailDomains = null;
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat != null && seat.table().room().getId().equals(roomId)) {
      MustNiuNiuTable table = seat.table();
      detailDomains = table.getScoreDetails();
    } else {
      detailDomains = roomCardNiuniuScoreDetailsDao.selectByRoomId(roomId);
    }
    return msgMgr.scoreDetailMsg(player, detailDomains);
  }

  /**
   * @Title findGameCfg.
   * @Description 获取游戏配置
   * @date 2018年5月31日 下午2:34:53
   * @param player .
   * @return .
   */
  public ResMessage findGameCfg(Player player) {
    MustNiuNiuRoomCardGameCfg gameCfg = roomCardGameCfgMgrScript.findGameCfg(this.game.getType(),
        MustNiuNiuRoomCardGameCfg.class);
    if (gameCfg == null) {
      LOG.error("[{}]房卡，游戏配置信息缺失", this.game.getDesc());
      HuohuaAssert.isTrue(false);
    }
    HuohuaAssert.isTrue(gameCfg != null);
    NiuNiuRoomCardGameCfgValue gameCfgValue = gameCfg.cfgs();
    if (gameCfgValue == null) {
      LOG.error("[{}]房卡，游戏配置信息详情缺失", this.game.getDesc());
      HuohuaAssert.isTrue(false);
    }
    return mustNiuNiuMsgMgr.gameCfgMsg(gameCfgValue);
  }



  /**
   * @Title resCreateRoomCard.
   * @Description 创建
   * @date 2018年6月8日 上午10:44:01
   * @return .
   */
  public ResMessage resCreateRoomCard(Player player, int group, int type, int seat, int round,
      long fee, int betOptionId, int startOptionId, int extrBetOptionId, int multipleId,
      List<Integer> specialMultipleIds, List<Integer> advanceOptionIds, int bankerChipId,
      int callBankerMultiple) {
    MustNiuNiuRoomCardGameCfg gameCfg = roomCardGameCfgMgrScript.findGameCfg(this.game.getType(),
        MustNiuNiuRoomCardGameCfg.class);
    if (gameCfg == null) {
      LOG.error("[{}]房卡，游戏配置信息缺失", this.game.getDesc());
      HuohuaAssert.isTrue(false);
    }
    HuohuaAssert.isTrue(gameCfg != null);
    NiuNiuRoomCardGameCfgValue gameCfgValue = gameCfg.cfgs();
    if (gameCfgValue == null) {
      LOG.error("[{}]房卡，游戏配置信息详情缺失", this.game.getDesc());
      HuohuaAssert.isTrue(false);
    }

    BasicGameInfoPo gameInfoPo = null;
    if (isNiuNiuBeBanker(type)) {
      gameInfoPo = gameCfgValue.getNiuniuBeBanker();
    } else if (isOwnerBeBanker(type)) {
      gameInfoPo = gameCfgValue.getOwnerBeBanker();
    } else if (isFreeCallBanker(type)) {
      gameInfoPo = gameCfgValue.getFreeCallBanker();
    } else if (isShowCardCallBanker(type)) {
      gameInfoPo = gameCfgValue.getShowCardCallBanker();
    } else {
      LOG.error("[{}]房卡，[{}]游戏类型不符合", this.game.getDesc(), type);
      HuohuaAssert.isTrue(false);
    }

    MustNiuNiuRoomCardRoomCfg roomCfg = toRoomCfg(player, gameInfoPo, gameCfgValue, group, type,
        seat, round, fee, betOptionId, startOptionId, extrBetOptionId, multipleId,
        specialMultipleIds, advanceOptionIds, bankerChipId, callBankerMultiple);

    boolean payNow = roomCfg.getBillType().equals(RoomCardPayByConstant.BY_OWNER);
    // 由房主支付,且未非手动开牌，创建房间时,由房主支付
    if (payNow) {
      long gold = roomCfg.getPrice();
      if (player.getGold() < gold) {
        LOG.error("[{}]房卡，金币不足,无法创建房间", this.game.getDesc());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_GOLD_NOT_ENOUGH);
      }
    }

    // 创建房卡
    MustNiuNiuRoom room = super.createRoomCard(roomCfg);
    LOG.info("游戏[{}]，玩家[{}]创建房卡[{}]成功", this.game.getDesc(), player.playerBo().getId(),
        JsonUtil.toJson(room));

    // 由房主支付，扣款
    if (payNow) {
      long gold = roomCfg.getPrice();
      // 扣款
      playerMgr.minusGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
      // 扣款记录
      DbLogService.log(new RoomCardFeeLog(player, this.game, room.getId(),
          RoomCardPayByConstant.BY_OWNER, gold, RoomCardFeeLogFeeType.PAY));
    }

    // 增加群中的房间数量
    Integer groupId = room.getRoomCfg().getGroupId();
    if (groupId != null && groupId > 0) {
      groupDao.incGroupRoomNum(groupId, 1);
    }
    return msgMgr.createRoomCardMsg(room.getId());
  }

  private MustNiuNiuRoomCardRoomCfg toRoomCfg(Player player, BasicGameInfoPo gameInfoPo,
      NiuNiuRoomCardGameCfgValue gameCfgValue, int groupId, int type, int seat, int round,
      long fee, int betOptionId, int startOptionId, int extrBetOptionId, int multipleId,
      List<Integer> specialMultipleIds, List<Integer> advanceOptionIds, int bankerChipId,
      int callBankerMultiple) {
    MustNiuNiuBetOptionPo betOptionPo = null;
    List<MustNiuNiuCardSpecialMultiplePo> specialMultiple = null;
    List<MustNiuNiuAdvanceOptionPo> advanceOptions = null;
    MustNiuNiuBankerChipPo bankerChipPo = null;
    MustNiuNiuCallBankerMultiplePo callBanerMultiplePo = null;

    // 下注选项
    betOptionPo = findBetOption(gameInfoPo.getBetOptions(), betOptionId);
    HuohuaAssert.isTrue(betOptionPo != null);

    // 座位
    MustNiuNiuSeatPo seatPo = findSeatPo(gameInfoPo.getSeats(), seat);
    HuohuaAssert.isTrue(seatPo != null);

    // 局数
    MustNiuNiuRoundPo roundPo = findRoundPo(gameInfoPo.getRounds(), round);
    HuohuaAssert.isTrue(roundPo != null);

    // 开局方式
    MustNiuNiuStartOptionPo startOption =
        findStartOptionPo(gameInfoPo.getStartOptions(), startOptionId);
    HuohuaAssert.isTrue(startOption != null);
    HuohuaAssert.isTrue(startOption.getPersons() == 0 // 手动开桌
        || CheckUtil.checkBetween(startOption.getPersons(), 2, seatPo.getSeat())); // 满多少人开桌

    // 推注
    MustNiuNiuExtrBetOptionPo extrBet =
        findExtrBetOptionPo(gameInfoPo.getExtrBetOptions(), extrBetOptionId);
    HuohuaAssert.isTrue(extrBet != null);

    // 倍数
    MustNiuNiuCardMultiplePo multiple = findMultiplePo(gameInfoPo.getMultiples(), multipleId);
    HuohuaAssert.isTrue(multiple != null);

    // 特殊玩法
    if (EmptyUtil.listIsNotEmpty(specialMultipleIds)) {
      specialMultiple =
          findSpecialMultiplePo(gameInfoPo.getSpecialMultiples(), specialMultipleIds);
      HuohuaAssert.isTrue(EmptyUtil.listIsNotEmpty(specialMultiple)
          && specialMultiple.size() == specialMultipleIds.size());
    }

    // 高级选项
    if (EmptyUtil.listIsNotEmpty(advanceOptionIds)) {
      advanceOptions = findAdvanceOptionPo(gameInfoPo.getAdvanceOptions(), advanceOptionIds);
      HuohuaAssert.isTrue(EmptyUtil.listIsNotEmpty(advanceOptions)
          && advanceOptions.size() == advanceOptionIds.size());
    }

    // 固定庄家
    if (gameInfoPo instanceof OwnerBeBankerGameInfoCfgPo) {
      OwnerBeBankerGameInfoCfgPo ownerBeBanker = (OwnerBeBankerGameInfoCfgPo) gameInfoPo;
      bankerChipPo = findBankerChipPo(ownerBeBanker.getBankerChips(), bankerChipId);
      HuohuaAssert.isTrue(bankerChipPo != null);
    }

    // 明牌抢庄
    if (gameInfoPo instanceof ShowCardCallBankerGameInfoCfgPo) {
      ShowCardCallBankerGameInfoCfgPo showCardCallBanker =
          (ShowCardCallBankerGameInfoCfgPo) gameInfoPo;
      callBanerMultiplePo = findCallBankerMultiplePo(showCardCallBanker.getCallBankerMultiples(),
          callBankerMultiple);
      HuohuaAssert.isTrue(callBanerMultiplePo != null);
    }

    MustNiuNiuRoomCardRoomCfgValue roomCfgValue = new MustNiuNiuRoomCardRoomCfgValue();
    roomCfgValue.setType(type);
    roomCfgValue.setBetOption(betOptionPo);
    roomCfgValue.setRound(roundPo);

    RoomCardMustNiuNiuFeeDomain feeDomain = findNiuNiuFeeDomain(type, seat, round, (int) fee);
    roomCfgValue.setFee(feeDomain);

    roomCfgValue.setStartOption(startOption);
    roomCfgValue.setSeat(seatPo);
    roomCfgValue.setExtrBetOption(extrBet);
    roomCfgValue.setMultiple(multiple);
    roomCfgValue.setSpecialMultiples(specialMultiple);
    roomCfgValue.setAdvanceOptions(advanceOptions);
    roomCfgValue.setBankerChip(bankerChipPo);
    roomCfgValue.setCallBankerMultiple(callBanerMultiplePo);

    Long playerId = player.playerBo().getId();
    Long superId = player.getSuperId();

    RoomCardGroupDomain groupDomain = null;
    if (groupId != 0) {
      groupDomain = roomCardGroupMapper.selectByPrimaryKey(groupId);
      // TODO:合并后1改成constant变量
      if (groupDomain == null || !groupDomain.getState().equals((byte) 1)) {
        LOG.error("[{}]群不存在或者解散,无法创建属于该群的房卡", this.game.getDesc());
        HuohuaAssert.isTrue(false, ErrorCode.GROUP_NO_EXIST);
      }
      RoomCardGroupMemberDomainExample example = new RoomCardGroupMemberDomainExample();
      example.createCriteria().andGroupIdEqualTo(groupId).andPlayerIdEqualTo(playerId);
      example.setLimit(1);
      List<RoomCardGroupMemberDomain> memList =
          roomCardGroupMemberMapper.selectByExample(example);
      HuohuaAssert.isTrue(EmptyUtil.listIsNotEmpty(memList) && memList.size() == 1,
          ErrorCode.GROUP_NOT_GROUP_MEM, "不能创建房间");
    }

    Game game = this.game;
    MustNiuNiuRoomCardRoomCfg cfg = new MustNiuNiuRoomCardRoomCfg();
    cfg.setPlayerId(playerId);
    cfg.setSuperId(superId);
    cfg.setPlayerAvatar(player.getPlayerAvatar());
    cfg.setGroupId(groupId);
    cfg.setGameId(game.getType());
    cfg.setGameName(game.getDesc());

    if (fee == feeDomain.getPayByAaFee()) {
      cfg.setPayBy(RoomCardPayByConstant.BY_AA); // AA支付
    } else {
      cfg.setPayBy(RoomCardPayByConstant.BY_OWNER); // 房主支付
    }
    cfg.setPrice(fee);

    cfg.setRenewTimes(0);
    cfg.setBillType(RoomCardBillTypeConstant.BY_GAME);
    cfg.setRechargeFee(0L);
    cfg.setLeftGame(roomCfgValue.getRound().getRound());
    cfg.setPlayGames(0);
    cfg.setLeftTime(0L);
    cfg.setCreateTime(new Date());
    cfg.setEndTime(null);
    cfg.setState(RoomCardStateConstant.NO_START);
    cfg.setRemove(RoomCardRemoveConstant.NORMAL);
    cfg.setGameCfg(JsonUtil.toJson(roomCfgValue));
    return cfg;
  }

  /**
   * 获取游戏最新信息 .
   */
  public ResMessage gameLastInfo(Player player) {
    AbstractRoom curRoom = player.curRoom;
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());

    ResMessage result = null;
    if (curRoom != null && seat != null) {
      result = msgMgr.gameLastInfo(seat);
    } else {
      result = new ResGameInfoMsg();
    }
    return result;
  }

  /**
   * 玩家进入房间 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public ResMessage enterRoom(Player player, int roomId, String pwd) {
    Long playerId = player.getId();
    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null) {
      Game game = curRoom.game();
      LOG.warn("[{}]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", this.game.getDesc(), playerId,
          player.getPlayerName(), game.getDesc(), curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, game.getDesc());
    }

    MustNiuNiuRoom room = super.getRoom(roomId);
    HuohuaAssert.isTrue(room != null, ErrorCode.GAME_ROOM_CLOSED);

    MustNiuNiuTable table = room.table();
    // 校验设置
    checkTableSetting(player, room.table(), pwd);

    MustNiuNiuSeat seat = super.findPlayerSeat(playerId);
    if (seat != null) {
      LOG.error("[{}]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    seat = findEmptySeat(player, room, true);
    if (seat == null) {
      LOG.warn("[{}]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", this.game.getDesc(), player.getPlayerName(),
          player.getId(), roomId, room.getMaxByStanderNum());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ROOM_FULL);
    }

    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setSex(player.getSex());
    seat.setRobot(player.robot());
    seat.setState(SeatState.SEATED);

    MustNiuNiuRoomCardRoomCfgValue cfgValue = room.getRoomCfg().cfgs();
    if (table.isOwnerBeBanker() && seat.getPlayerId() == table.room().getOwnerId()) {
      seat.setTotalChips(cfgValue.getBankerChip().getChips()); // 房主坐庄,分数为庄家筹码
    } else {
      seat.setTotalChips(0);
    }

    // 修改玩家当前房间
    player.curRoom = room;
    // 进入房间时间
    player.enterRoomTime = System.currentTimeMillis();
    // 玩家的当前桌子
    player.curTable = table;
    player.enterTableTime = System.currentTimeMillis();
    player.curSeat = seat;

    // 更新玩家座位
    super.updatePlayerSeat(playerId, seat);
    // 房间中的玩家
    if (!room.getPlayers().contains(player.getId())) {
      room.getPlayers().add(player.getId());
    }

    // 如果为固定庄家房主进入房间，直接坐下
    if (table.isOwnerBeBanker() && table.room().getOwnerId() == player.playerBo().getId()) {
      // 判断金币是否足够
      MustNiuNiuRoomCardRoomCfg roomCfg = table.room().getRoomCfg();
      long gold = roomCfg.getPrice();
      if (roomCfg.getPayBy().equals(RoomCardPayByConstant.BY_AA)) {
        if (player.getGold() < gold) {
          LOG.error("[{}]房卡，金币不足,无法坐下", this.game.getDesc());
          HuohuaAssert.isTrue(false, ErrorCode.GAME_GOLD_NOT_ENOUGH);
        }

        // 扣款
        playerMgr.minusGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
        // 扣款记录
        DbLogService.log(new RoomCardFeeLog(player, this.game, player.curRoom.getId(),
            RoomCardPayByConstant.BY_AA, gold, RoomCardFeeLogFeeType.PAY));
      }

      doSeatDown(player, SeatState.READY);
    }

    LOG.info("[{}]玩家[{}][{}]进入房卡[{}]成功", this.game.getDesc(), player.getPlayerName(),
        player.getId(), roomId);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title seatDown.
   * @Description 玩家坐下
   * @date 2018年6月8日 上午10:58:33
   * @param player .
   * @return .
   */
  public ResMessage seatDown(Player player) {
    if (player.curRoom == null) {
      LOG.warn("[{}]玩家[{}][{}]尚未进入房卡,不能坐下", this.game.getDesc(), player.getPlayerName(),
          player.getId(), game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_IN, game.getDesc());
    }
    if (!(player.curRoom instanceof MustNiuNiuRoom)) {
      LOG.error("[{}]玩家[{}][{}]当前不在房间中不能坐下", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, player.curRoom.game().getDesc());
    }
    MustNiuNiuSeat curSeat = (MustNiuNiuSeat) player.curSeat;
    if (!curSeat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_ALREADY_SEAT_DOWN);
    }
    MustNiuNiuRoom curRoom = (MustNiuNiuRoom) player.curRoom;
    MustNiuNiuTable curTable = (MustNiuNiuTable) player.curTable;

    MustNiuNiuRoomCardRoomCfg roomCfg = curRoom.getRoomCfg();
    MustNiuNiuRoomCardRoomCfgValue roomCfgValue = roomCfg.cfgs();
    // 房卡已经开桌，判断是否中途禁入
    if (curRoom.table().getStatus() != MustNiuNiuGameState.NO_START) {
      MustNiuNiuAdvanceOptionPo joinDisAble =
          curTable.findAdvanceOptionPo(MustNiuNiuAdvanceOption.JOIN_DISABLE);
      // 判断是否中途禁入
      if (joinDisAble != null) {
        LOG.error("[{}]玩家[{}][{}]中途禁止进入房卡[{}]", this.game.getDesc(), player.getPlayerName(),
            player.getId(), curRoom.getId());
        HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_JOIN_DISABLE);
      }
    }
    // 判断金币是否足够
    long gold = roomCfg.getPrice();
    if (roomCfg.getPayBy().equals(RoomCardPayByConstant.BY_AA)) {
      if (player.getGold() < gold) {
        LOG.error("[{}]房卡，金币不足,无法坐下", this.game.getDesc());
        HuohuaAssert.isTrue(false, ErrorCode.GAME_GOLD_NOT_ENOUGH);
      }

      // 扣款
      playerMgr.minusGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
      // 扣款记录
      DbLogService.log(new RoomCardFeeLog(player, this.game, curRoom.getId(),
          RoomCardPayByConstant.BY_AA, gold, RoomCardFeeLogFeeType.PAY));
    }

    MustNiuNiuTable table = curRoom.table();
    // 游戏未开桌
    if (table.getStatus() == MustNiuNiuGameState.NO_START) { // 未开始进入时，状态为准备
      doSeatDown(player, SeatState.READY);
      // 如果开桌方式为满多少开桌
      int startPerson = roomCfgValue.getStartOption().getPersons();
      if (startPerson > 0 && table.getGameSeats(true).size() >= startPerson) {
        delayStartGame(table, 1, TimeUnit.SECONDS);
      }
    } else if (table.getStatus() == MustNiuNiuGameState.READY) { // 游戏为准备阶段，状态为准备
      doSeatDown(player, SeatState.READY);
      // 如果所有人都准备好了,进入下一阶段
      if (readiedOver(table)) {
        if (stepFutureDisable(table.stepFuture)) {
          table.stepFuture = scheduleMgr.schedule(() -> {
            doCallBankerStage(table);
          }, 1, TimeUnit.SECONDS, this.gameExecutor);
        }
      }
    } else { // 其他阶段进入时，状态为就坐
      doSeatDown(player, SeatState.SEATED);
    }

    return ResMessage.DEFAULT;
  }

  /**
   * 玩家准备 .
   * 
   * @param player 玩家.
   */
  public ResMessage ready(Player player) {
    if (player.curRoom == null) {
      LOG.warn("[{}]玩家[{}][{}]尚未进入房卡,不能准备", this.game.getDesc(), player.getPlayerName(),
          player.getId(), game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_IN, game.getDesc());
    }
    if (!(player.curRoom instanceof MustNiuNiuRoom)) {
      LOG.error("[{}]玩家[{}][{}]当前不在房间中不能准备", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, player.curRoom.game().getDesc());
    }

    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能准备", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 校验是否坐下
    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能准备", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_IS_STANDERBY, "无法准备");
    }

    // 校验游戏状态(准备阶段才能准备)
    MustNiuNiuRoom room = (MustNiuNiuRoom) player.curRoom;
    if (!room.table().getStatus().equals(MustNiuNiuGameState.READY)) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中游戏不为准备状态，无法准备", this.game.getDesc(),
          player.getPlayerName(), player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_READY_STAGE, "无法准备");
    }

    if (seat.readied()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]已经准备好,请勿重复准备", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      return ResMessage.DEFAULT;
    }

    // 坐下
    doReady(seat);

    LOG.error("[{}]玩家[{}][{}]在牌桌[{}]准备好", this.game.getDesc(), player.getPlayerName(),
        player.getId(), player.curRoom.getId());

    return ResMessage.DEFAULT;
  }

  private void doReady(MustNiuNiuSeat seat) {
    // 玩家就坐
    seat.setState(SeatState.READY);
    msgMgr.sendReadyMsg(seat);

    MustNiuNiuTable table = seat.table();
    if (readiedOver(table)) { //
      if (stepFutureDisable(table.stepFuture)) {
        gameExecutor.execute(() -> {
          if (table.isNiuNiuBeBanker() || table.isOwnerBeBanker()) {
            doBetStage(table);
          } else if (table.isFreeCallBanker() || table.isShowCardCallBanker()) {
            // 游戏进入叫庄状态
            doCallBankerStage(table);
          }
        });
      }
    }
  }

  /**
   * @Title startGame.
   * @Description 开始游戏
   * @date 2018年6月8日 上午10:59:02
   * @param player .
   * @return .
   */
  public ResMessage startGame(Player player) {
    if (player.curRoom == null) {
      LOG.warn("[{}]玩家[{}][{}]尚未进入房卡,不能开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId(), game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_IN, game.getDesc());
    }
    if (!(player.curRoom instanceof MustNiuNiuRoom)) {
      LOG.error("[{}]玩家[{}][{}]当前不在房间中不能开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, player.curRoom.game().getDesc());
    }

    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 游戏状态为未开始
    MustNiuNiuRoom room = (MustNiuNiuRoom) player.curRoom;
    if (room.table().getStatus() != MustNiuNiuGameState.NO_START) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中不能重复开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_GAME_ALREADY_START, "不能重复开桌");
    }

    // 校验是否为房主
    if (!room.getOwnerId().equals(player.playerBo().getId())) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中非房主不能开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_THE_OWNER, "不能开桌");
    }

    MustNiuNiuTable table = room.table();
    int num = 0;
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState() == SeatState.READY) {
        num++;
      }
    }

    // 校验人数是否足够
    if (num < 2) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中人数为[{}]不能开桌", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId(), num);
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_PLAYER_NOT_ENOUGH, "不能开桌");
    }

    // 游戏开桌
    delayStartGame(table, 1, TimeUnit.SECONDS);

    return ResMessage.DEFAULT;
  }

  /**
   * 玩家叫庄家 .
   * 
   * @param player 玩家.
   * @param multiple 叫庄倍数(0:不叫庄).
   */
  public ResMessage callDealer(Player player, int multiple) {
    if (player.curRoom == null) {
      LOG.warn("[{}]玩家[{}][{}]尚未进入房卡,不能叫庄", this.game.getDesc(), player.getPlayerName(),
          player.getId(), game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_IN, game.getDesc());
    }
    if (!(player.curRoom instanceof MustNiuNiuRoom)) {
      LOG.error("[{}]玩家[{}][{}]当前不在房间中不能叫庄", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, player.curRoom.game().getDesc());
    }

    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能叫庄", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 校验是否坐下
    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能叫庄", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_IS_STANDERBY, "无法叫庄");
    }

    // 校验游戏的状态
    MustNiuNiuTable table = seat.table();
    if (table.getStatus() != MustNiuNiuGameState.CALL_BANKER) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中不能叫庄,游戏阶段[{}]", this.game.getDesc(),
          player.getPlayerName(), player.getId(), player.curRoom.getId(), table.getStatus().desc);

      HuohuaAssert.isTrue(false);
    }

    // 校验是否已经叫庄
    Integer callDealerMultiple = seat.getCallDealerMultiple();
    if (callDealerMultiple != null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中不能重复叫庄", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());

      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_ALREADY_CALL_DEALER);
    }

    // 校验叫庄倍数是否合法
    if (!checkCallBankerMultiple(table, multiple)) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中叫庄倍数[{}]不合法", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId(), multiple);

      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_CALL_DEALER_MULTIPLE_ILLEGAL);
    }

    // 执行叫庄操作
    doCallDealer(seat, multiple);

    return ResMessage.DEFAULT;
  }

  private void doCallDealer(MustNiuNiuSeat seat, int multiple) {
    // 叫庄过程
    seat.setCallDealerMultiple(multiple);
    msgMgr.sendCallDealerMsg(seat, multiple);

    LOG.info("[{}]玩家[{}][{}]叫庄成功，倍数为[{}]", this.game.getDesc(), seat.getPlayerName(),
        seat.getPlayerId(), multiple);

    MustNiuNiuTable table = seat.table();
    // 所有人叫庄结束
    if (table.callDealerOver()) {
      // 选举庄家
      this.gameExecutor.execute(() -> {
        // 随机产生庄家
        MustNiuNiuSeat dealerSeat = table.randCallDealerSeat();
        seatBeDealer(dealerSeat);
        // 进入下一个阶段
        if (stepFutureDisable(table.stepFuture)) {
          gameExecutor.execute(() -> {
            // 进入下注阶段
            doBetStage(table);
          });
        }
      });
    }
  }

  /**
   * @Title seatBeDealer.
   * @Description 座位坐庄
   * @date 2018年6月9日 下午4:57:26
   * @param dealerSeat .
   */
  private void seatBeDealer(MustNiuNiuSeat dealerSeat) {
    // 这一把是庄家，下一把如果是闲家，不能推注
    dealerSeat.setPreWinBetSelf(0L);
    // 都不叫庄,叫庄倍数为1倍
    if (dealerSeat.getCallDealerMultiple() == null || dealerSeat.getCallDealerMultiple() == 0) {
      dealerSeat.setCallDealerMultiple(1);
    }
    MustNiuNiuTable table = dealerSeat.getTable();
    // 记录叫庄倍数
    table.setCallDealerMultiple(dealerSeat.getCallDealerMultiple());
    // 记住庄家编号
    table.setPreDealerId(dealerSeat.getPlayerId());
    // 设置为庄家
    dealerSeat.setDealer(true);

    // 根据情况清空推注选项
    MustNiuNiuAdvanceOptionPo betLimitOption =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_LIMIT);

    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      Integer callMultiple = s.getCallDealerMultiple();
      if (s.getState() == SeatState.GAMING) {
        // 庄家已经确定,尚未叫庄的玩家不抢庄(牛牛上庄|固定庄家)
        if (callMultiple == null) {
          s.setCallDealerMultiple(0);
        }
        // 如果有下注限制,抢庄倍数比庄家抢庄倍数小的不能推注
        if (betLimitOption != null) {
          if (callMultiple == null || callMultiple < table.getCallDealerMultiple()
              || callMultiple == 0) {
            s.setPreWinBetSelf(0L);
          }
        }
      }
    }
    // 发送确认庄家消息
    msgMgr.sendDealBankerMsg(dealerSeat);
  }

  /**
   * @Title doubleBet.
   * @Description 加倍
   * @date 2018年6月4日 下午9:19:22
   * @param player .
   * @return .
   */
  public ResMessage doubleBet(Player player) {
    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 校验是否坐下
    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_IS_STANDERBY, "无法加倍");
    }

    if (!seat.readied()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未准备不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_READY, "无法加倍");
    }

    if (seat.getCallDealerMultiple() == null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未叫庄不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_CALL_DEALER, "无法加倍");
    }

    MustNiuNiuTable table = seat.table();
    // 校验桌子状态
    if (table.getStatus() != MustNiuNiuGameState.BET) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中非下注阶段不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_BET_STAGE, "无法加倍");
    }

    MustNiuNiuAdvanceOptionPo betMultipleOptionPo =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_MULTIPLE);
    if (betMultipleOptionPo == null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未开启加倍,不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "未开启加倍");
    }

    if (seat.isDealer()) {
      LOG.error("[{}]玩家[{}][{}]是庄家不能加倍", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "庄家不能加倍");
    }

    if (seat.getBetedchips() > 0) {
      LOG.error("[{}]玩家[{}][{}]已经下过注", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "不能加倍");
    }

    if (seat.getCallDealerMultiple() != table.dealer().getCallDealerMultiple()) {
      LOG.error("[{}]玩家[{}][{}]不满足加倍条件", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "不满足加倍条件");
    }

    seat.setDoubleBet(true);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title bet.
   * @Description
   * @date 2018年6月8日 上午10:59:39
   * @param player 玩家
   * @param chips 下注筹码
   * @return .
   */
  public ResMessage bet(Player player, long chips) {
    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 校验是否坐下
    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_IS_STANDERBY, "无法下注");
    }

    if (!seat.readied()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未准备不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_READY, "无法下注");
    }

    if (seat.getCallDealerMultiple() == null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未叫庄不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_CALL_DEALER, "无法下注");
    }

    MustNiuNiuTable table = seat.table();
    // 校验桌子状态
    if (table.getStatus() != MustNiuNiuGameState.BET) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中非下注阶段不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_BET_STAGE, "无法下注");
    }

    if (seat.isDealer()) {
      LOG.error("[{}]玩家[{}][{}]是庄家不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "庄家不能下注");
    }

    if (seat.getBetedchips() > 0) {
      LOG.error("[{}]玩家[{}][{}]已经下过注", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "不能重复下注");
    }

    BetTypeBo betType = checkBetChips(seat, chips);
    if (betType == BetTypeBo.ERROR) {
      LOG.error("[{}]玩家[{}][{}]下注[{}]不合法", this.game.getDesc(), player.getPlayerName(),
          player.getId(), chips);
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "下注选项不合法");
    }

    doBet(seat, chips);
    return ResMessage.DEFAULT;
  }

  private void doBet(MustNiuNiuSeat seat, long chips) {
    seat.setBetedchips(chips);
    seat.setBetType(checkBetChips(seat, chips));
    seat.setPreWinBetSelf(0L); // 清空推注
    msgMgr.sendBetMsg(seat, false);

    MustNiuNiuTable table = seat.table();
    if (table.betOver()) {
      LOG.info("[{}]闲家[{}][{}]下注[{}]结束,开始洗牌发牌", this.game.getDesc(), seat.getPlayerName(),
          seat.getPlayerId(), chips);
      // 进入下一个阶段
      if (stepFutureDisable(table.stepFuture)) {
        gameExecutor.execute(() -> {
          // 进入下注阶段
          doSendOpenCardStage(table);
        });
      }
    }
  }

  /**
   * 玩家下注 .
   * 
   * @param player 玩家.
   * @param chips .
   * @param otherPlayerId 被买码玩家编号.
   */
  public ResMessage betOther(Player player, long otherPlayerId, long chips) {
    // 校验位置
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false);
    }

    // 校验是否坐下
    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_IS_STANDERBY, "无法买码");
    }

    if (!seat.readied()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未准备不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_READY, "无法买码");
    }

    if (seat.getCallDealerMultiple() == null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未叫庄不能下注", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NO_CALL_DEALER, "无法买码");
    }

    MustNiuNiuTable table = seat.table();
    // 校验桌子状态
    if (table.getStatus() != MustNiuNiuGameState.BET) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中非下注阶段不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_NOT_BET_STAGE, "不能买码");
    }

    if (seat.isDealer()) {
      LOG.error("[{}]玩家[{}][{}]是庄家不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_BET_ERROR, "庄家不能买码");
    }

    MustNiuNiuAdvanceOptionPo betOther =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_OTHER);
    if (betOther == null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "不支持买码");
    }

    Player otherPlayer = playerMgr.getPlayer(otherPlayerId);
    if (otherPlayer == null) {
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "对方登录");
    }
    // 校验位置
    MustNiuNiuSeat otherSeat = super.findPlayerSeat(otherPlayerId);
    if (otherSeat == null) {
      LOG.error("[{}]玩家[{}][{}]还未进入牌桌[{}]不能被下注", this.game.getDesc(), otherPlayer.getPlayerName(),
          otherPlayer.getId(), otherPlayer.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "对方不在房间中");
    }

    // 校验是否坐下
    if (otherSeat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中旁观不能被下注", this.game.getDesc(), otherPlayer.getPlayerName(),
          otherPlayer.getId(), otherPlayer.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "对方为旁观者");
    }

    if (!otherSeat.readied()) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中未准备不能被下注", this.game.getDesc(),
          otherPlayer.getPlayerName(), otherPlayer.getId(), otherPlayer.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "对方未准备");
    }
    MustNiuNiuBetOptionPo betOption = table.findBetOptionPo();
    if (!betOption.getBetChips().contains((int) chips)) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中买码金币[{}]不正确，不能买码", this.game.getDesc(),
          otherPlayer.getPlayerName(), otherPlayer.getId(), otherPlayer.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "买码金币不正确");
    }

    if (seat.getBetOtherPlayerId() != null) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中不能重复买码", this.game.getDesc(), otherPlayer.getPlayerName(),
          otherPlayer.getId(), otherPlayer.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_COULD_NOT_BET_OTHER, "不能重复买码");
    }

    doBetOther(seat, otherPlayerId, chips);

    return ResMessage.DEFAULT;
  }

  private void doBetOther(MustNiuNiuSeat seat, long otherPlayerId, long chips) {
    if (otherPlayerId == 0L) {
      // 买码流程
      seat.setBetOtherPlayerId(0L);
      seat.setBetOtherBetChips(0L);
    } else {
      // 买码流程
      seat.setBetOtherPlayerId(otherPlayerId);
      seat.setBetOtherBetChips(chips);
      // 发送买码消息
      msgMgr.sendBetMsg(seat, true);
    }

    MustNiuNiuTable table = seat.table();
    if (table.betOver()) {
      LOG.info("[{}]闲家[{}][{}]下注[{}]结束,开始洗牌发牌", this.game.getDesc(), seat.getPlayerName(),
          seat.getPlayerId(), chips);
      // 进入下一个阶段
      if (stepFutureDisable(table.stepFuture)) {
        gameExecutor.execute(() -> {
          // 进入下注阶段
          doSendOpenCardStage(table);
        });
      }
    }
  }

  /**
   * 玩家摊牌 .
   * 
   * @param player 玩家.
   * @param playerCardIds . 玩家选择的3张牌的组合,如果玩家自己选择的牌是最优的牌则用玩家的
   */
  public ResMessage showdown(Player player, List<Integer> playerCardIds) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能摊牌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_SHOW_CARD_ERROR, "未进入房间");
    }

    if (!seat.getState().equals(SeatState.GAMING)) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能摊牌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_SHOW_CARD_ERROR, "未坐下,不能摊牌");
    }

    MustNiuNiuTable table = seat.table();
    // 校验桌子状态
    if (table.getStatus() != MustNiuNiuGameState.OPEN_CARD) {
      LOG.error("[{}]玩家[{}][{}]在牌桌[{}]中非开牌阶段不能买码", this.game.getDesc(), player.getPlayerName(),
          player.getId(), player.curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_SHOW_CARD_ERROR, "非摊牌阶段");
    }

    if (seat.isShowdown()) {
      LOG.error("[{}]玩家[{}][{}]重复摊牌", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    // 摊牌
    doShowdown(seat);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title doShowdown.
   * @Description 摊牌
   * @date 2018年6月5日 下午2:40:14
   * @param seat .
   */
  private void doShowdown(MustNiuNiuSeat seat) {
    seat.setShowdown(true);
    // seat.stepFuture.cancel(false);
    msgMgr.sendShowdownMsg(seat);

    MustNiuNiuTable table = seat.getTable();
    LOG.info("[{}]玩家[{}][{}]摊牌", this.game.getDesc(), seat.getPlayerName(), seat.getPlayerId());

    if (table.showdown()) { // 所有玩家都摊牌，进入下一个阶段
      // 延时进入下一个阶段，方便客户端展示庄家牌
      if (stepFutureDisable(table.stepFuture)) {
        table.stepFuture = this.scheduleMgr.schedule(() -> {
          // 进入下注阶段
          doShowResultStage(table);
        }, 2, TimeUnit.SECONDS, gameExecutor);
      }
    }
  }

  /**
   * @Title applyCloseRoom.
   * @Description 申请
   * @date 2018年6月8日 上午11:00:39
   * @param player .
   * @return .
   */
  public ResMessage applyCloseRoom(Player player) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能申请关闭房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_APPLY_CLOSE_ROOM_ERROR, "未进入房间");
    }

    MustNiuNiuTable table = seat.table();
    MustNiuNiuRoom room = table.room();

    // 非房主,需要坐下才能关闭房间
    if (!seat.table().room().getOwnerId().equals(player.getId())) {
      if (table.getStatus().equals(MustNiuNiuGameState.NO_START) && seat.isByStander()) {
        LOG.error("[{}]玩家[{}][{}]非房主,游戏尚未开始不能关闭房间", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_APPLY_CLOSE_ROOM_ERROR, "非游戏中,不能关闭房间");
      }
    }

    // 申请关闭次数限制
    if (seat.getHadApplyCloseRoom() != null) {
      LOG.error("[{}]玩家[{}][{}]非房主,操作过于频繁", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_APPLY_CLOSE_ROOM_ERROR, "操作过于频繁");
    }
    seat.setHadApplyCloseRoom(true);

    // 游戏未开始(房主申请关闭)
    if (table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      // 退款
      returnBackGold(table);
      msgMgr.sendCloseRoomCardMsg(table, CloseType.NO_START_BY_OWNER);
      // 游戏结束
      gameOver(room);
    } else {
      if (seat.isByStander()) {
        LOG.error("[{}]玩家[{}][{}]旁观中，不能申请关闭房间", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_APPLY_CLOSE_ROOM_ERROR, "旁观中，不能申请关闭房间");
      }
      for (MustNiuNiuSeat s : table.getGameSeats(true)) {
        if (s.getState().equals(SeatState.GAMING)) {
          s.setAnswerCloseRoom(null);
        }
      }
      // 游戏已经开始
      seat.setAnswerCloseRoom(true);
      // 定时不同意关闭房间
      scheduleDisagreeCloseRoom(table, ANSER_TIME, TimeUnit.SECONDS);
      msgMgr.sendApplyCloseRoomCardMsg(seat, ANSER_TIME);
    }
    // 游戏已经开始
    return ResMessage.DEFAULT;
  }

  /**
   * @Title resExitRoom.
   * @Description 退出房间
   * @date 2018年6月8日 上午11:03:39
   * @param player .
   * @return .
   */
  public ResMessage resExitRoom(Player player) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能申请关闭房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      return ResMessage.DEFAULT;
    }
    MustNiuNiuTable table = seat.table();
    if (!seat.isByStander() && !table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      LOG.error("[{}]玩家[{}][{}]游戏进行中,不能退出房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_EXIT_ROOM_ERROR, "游戏进行中,不能退出房间");
    }
    // 游戏尚未开始，已经坐下,退回房费
    if (!seat.isByStander() && table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      MustNiuNiuRoomCardRoomCfg roomCfg = table.room().getRoomCfg();
      if (roomCfg.getPayBy().equals(RoomCardPayByConstant.BY_AA)) { // AA制才退款
        returnBackGold(seat);
      }
    }

    // 移除托管玩家
    table.getTrustees().remove(player.getId());
    // 发送退出桌子信息
    msgMgr.sendExitRoomCardMsg(seat);
    // 释放玩家房间桌子位置
    super.releasePlayerRoomTableSeat(player);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title scheduleDisagreeCloseRoom.
   * @Description 定时不同意关闭房间
   * @date 2018年6月8日 下午2:51:27
   * @param table .
   */
  private void scheduleDisagreeCloseRoom(MustNiuNiuTable table, int delay, TimeUnit unit) {
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState().equals(SeatState.GAMING) && s.getAnswerCloseRoom() == null) {
        if (stepFutureDisable(s.stepFuture)) {
          s.stepFuture = this.scheduleMgr.schedule(() -> {
            doAnswerApplyCloseRoom(s, false);
          }, delay, unit, this.gameExecutor);
        }
      }
    }
  }

  private void cancelDisagreeCloseRoomSchedule(MustNiuNiuTable table) {
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState().equals(SeatState.GAMING) && s.getAnswerCloseRoom() == null) {
        stepFutureDisable(s.stepFuture);
      }
    }
  }

  private void returnBackGold(MustNiuNiuTable table) {
    MustNiuNiuRoomCardRoomCfg roomCfg = table.room().getRoomCfg();
    long gold = roomCfg.getPrice();
    if (roomCfg.getPayBy().equals(RoomCardPayByConstant.BY_AA)) {
      for (MustNiuNiuSeat seat : table.getGameSeats(true)) {
        if (!seat.isByStander()) {
          Player player = playerMgr.getPlayer(seat.getPlayerId());
          // 退款
          playerMgr.addGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
          roomCfg.setRechargeFee(roomCfg.getRechargeFee() - gold);
          // 退款款记录
          DbLogService.log(new RoomCardFeeLog(player, this.game, table.room().getId(),
              RoomCardPayByConstant.BY_AA, gold, RoomCardFeeLogFeeType.RETURN_BACK));
        }
      }
    } else {
      Long ownerId = table.room().getOwnerId();
      PlayerBo player = playerMgr.selectPlayer(ownerId);
      // 退款款
      playerMgr.addGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
      // 退款款记录
      DbLogService.log(new RoomCardFeeLog(player, this.game, table.room().getId(),
          RoomCardPayByConstant.BY_OWNER, gold, RoomCardFeeLogFeeType.RETURN_BACK));
    }
  }

  private void returnBackGold(MustNiuNiuSeat seat) {
    MustNiuNiuTable table = seat.table();
    MustNiuNiuRoomCardRoomCfg roomCfg = table.room().getRoomCfg();
    long gold = roomCfg.getPrice();
    if (roomCfg.getPayBy().equals(RoomCardPayByConstant.BY_AA)) {
      if (!seat.isByStander()) {
        Player player = playerMgr.getPlayer(seat.getPlayerId());
        // 退款
        playerMgr.addGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
        roomCfg.setRechargeFee(roomCfg.getRechargeFee() - gold);
        // 退款款记录
        DbLogService.log(new RoomCardFeeLog(player, this.game, table.room().getId(),
            RoomCardPayByConstant.BY_AA, gold, RoomCardFeeLogFeeType.RETURN_BACK));
      }
    } else {
      Long ownerId = table.room().getOwnerId();
      if (ownerId == seat.getPlayerId()) {
        PlayerBo player = playerMgr.selectPlayer(ownerId);
        // 退款
        playerMgr.addGold(player, gold, LogReason.ROOM_CARD_MUST_NIU_NIU_FEE);
        // 退款款记录
        DbLogService.log(new RoomCardFeeLog(player, this.game, table.room().getId(),
            RoomCardPayByConstant.BY_OWNER, gold, RoomCardFeeLogFeeType.RETURN_BACK));
      }
    }
  }

  /**
   * @Title answerApplyCloseRoom.
   * @Description 应答是否同意关闭房间
   * @date 2018年6月8日 上午10:36:38
   * @param player 玩家编号
   * @param agree 1:同意,0:不同意
   * @return ResMessage
   */
  public ResMessage answerApplyCloseRoom(Player player, boolean agree) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能关闭房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_CLOSE_ROOM_ERROR, "未进入房间");
    }

    MustNiuNiuTable table = (MustNiuNiuTable) player.curTable;
    if (table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      LOG.error("[{}]玩家[{}][{}]非房主,游戏尚未开始不能关闭房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_CLOSE_ROOM_ERROR, "游戏尚未开始,不能应答关闭房间");
    }

    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]旁观中，不能申请关闭房间", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.ROOMCARD_CLOSE_ROOM_ERROR, "盘观者, 不能应答关闭房间");
    }

    // 处理应答
    doAnswerApplyCloseRoom(seat, agree);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title offBanker.
   * @Description 下庄
   * @date 2018年6月8日 上午11:04:05
   * @param player .
   * @return .
   */
  public ResMessage offBanker(Player player) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中,不能下庄", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    MustNiuNiuTable table = seat.table();
    if (!table.isOwnerBeBanker()) {
      LOG.error("[{}]玩家[{}][{}]非固定庄家,不能下庄", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      LOG.error("[{}]玩家[{}][{}]非开始,不能下庄", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (table.room().getRoomCfg().getPlayGames() < 4) {
      LOG.error("[{}]玩家[{}][{}]未玩三局,不能下庄", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    msgMgr.sendCloseRoomCardMsg(table, CloseType.OWNER_BE_BANKER_OFF_BANKER);

    MustNiuNiuRoom room = seat.table().room();
    gameOver(room);

    return ResMessage.DEFAULT;
  }

  /**
   * @Title trustee.
   * @Description 托管
   * @date 2018年6月8日 上午11:02:02
   * @param player 玩家
   * @param trusteeBo 托管选项
   * @return .
   */
  public ResMessage trustee(Player player, TrusteeBo trusteeBo) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中,不能托管", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]为旁观者,不能托管", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (!checkTrustee(player, seat, trusteeBo)) {
      LOG.error("[{}]玩家[{}][{}]校验数据有误", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    MustNiuNiuTable table = seat.table();
    table.getTrustees().put(player.getId(), trusteeBo);
    seat.setSysHost(true);

    // 发送托管变化消息
    msgMgr.sendTrusteeChangeMsg(seat, true);

    if (table.getStatus().equals(MustNiuNiuGameState.READY)
        && seat.getState().equals(SeatState.SEATED)) {
      doReady(seat);
    } else if (table.getStatus().equals(MustNiuNiuGameState.CALL_BANKER)) {
      trusteeCallDealerSeat(seat, trusteeBo);
    } else if (table.getStatus().equals(MustNiuNiuGameState.BET)) {
      trusteeBetSeat(seat, trusteeBo);
    } else if (table.getStatus().equals(MustNiuNiuGameState.OPEN_CARD)) {
      if (seat.getState().equals(SeatState.GAMING) && !seat.isShowdown()) {
        doShowdown(seat);
      }
    }
    return ResMessage.DEFAULT;
  }

  private boolean checkTrustee(Player player, MustNiuNiuSeat seat, TrusteeBo trusteeBo) {
    if (trusteeBo == null) {
      return false;
    }

    TrusteeCallBankerOptionBo callBanker = trusteeBo.getCallBankerOption();
    MustNiuNiuTable table = seat.table();
    if (table.isFreeCallBanker()) {
      if (callBanker == null) {
        LOG.error("[{}]玩家[{}][{}]托管，叫庄选项不能为空", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        return false;
      }
      if (callBanker.getNiu() != 0 && callBanker.getNiu() != -1) {
        LOG.error("[{}]玩家[{}][{}]托管，叫庄选项中的niu值有误,为[{}]", this.game.getDesc(),
            player.getPlayerName(), player.getId(), callBanker.getNiu());
        return false;
      }
    } else if (table.isShowCardCallBanker()) {
      if (callBanker == null) {
        LOG.error("[{}]玩家[{}][{}]托管，叫庄选项不能为空", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        return false;
      }
      if (!CheckUtil.checkBetween(callBanker.getNiu(), 0, 10) && callBanker.getNiu() != -1) {
        LOG.error("[{}]玩家[{}][{}]托管，叫庄选项中的niu值有误,为[{}]", this.game.getDesc(),
            player.getPlayerName(), player.getId(), callBanker.getNiu());
        return false;
      }

      MustNiuNiuCallBankerMultiplePo maxCallDealerMutiplePo = table.findMaxCallBankerMultiple();
      int maxCallDealerMultiple = maxCallDealerMutiplePo.getMultiple();
      if (!CheckUtil.checkBetween(callBanker.getMultiple(), 0, maxCallDealerMultiple)) {
        LOG.error("[{}]玩家[{}][{}]托管，叫庄选项中的叫庄倍数有误,为[{}],最高抢庄倍数[{}]", this.game.getDesc(),
            player.getPlayerName(), player.getId(), callBanker.getMultiple(),
            maxCallDealerMultiple);
        return false;
      }
    } else if (table.isOwnerBeBanker() || table.isNiuNiuBeBanker()) { // 固定庄家|牛牛坐庄,不能叫庄
      if (callBanker != null) {
        LOG.error(
            "游戏[{}]类型[{}]玩家[{}][{}]托管，不能包含叫庄选项", this.game.getDesc(), table.room().getRoomCfg()
                .cfgs().getType() == MustNiuNiuGame.NIUNIU_BE_BANKER.intValue() ? "牛牛坐庄" : "固定庄家",
            player.getPlayerName(), player.getId());
        return false;
      }
    }
    // 推注
    TrusteeExtrBetOptionBo extrBetOption = trusteeBo.getExtrBetOption();
    MustNiuNiuExtrBetOptionPo extrBetAdvanceOption = table.findExtrBetOptionPo();
    if (extrBetAdvanceOption != null && extrBetAdvanceOption.getMultiple() > 0) {
      if (extrBetOption == null) {
        LOG.error("[{}]玩家[{}][{}]托管,推注选项不能空", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        return false;
      }
      int niu = extrBetOption.getNiu();
      if (table.isShowCardCallBanker()) {
        if (!CheckUtil.checkBetween(niu, -1, 10)) {
          LOG.error("[{}]玩家[{}][{}]托管，推注选项中的niu值有误,为[{}]", this.game.getDesc(),
              player.getPlayerName(), player.getId(), niu);
          return false;
        }
      } else {
        if (!CheckUtil.checkBetween(niu, -1, 0)) {
          LOG.error("[{}]玩家[{}][{}]托管，推注选项中的niu值有误,为[{}]", this.game.getDesc(),
              player.getPlayerName(), player.getId(), niu);
          return false;
        }
      }
    } else {
      if (extrBetOption != null) {
        LOG.error("[{}]玩家[{}][{}]托管，未开启推注,不能包含推注选项", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        return false;
      }
    }

    // 下注
    TrusteeBetOptionBo betOption = trusteeBo.getBetOption();
    MustNiuNiuBetOptionPo betOptionPo = table.findBetOptionPo();
    if (betOption == null) {
      LOG.error("[{}]玩家[{}][{}]托管，下注选项不能为空", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      return false;
    }
    int niu = betOption.getNiu();
    if (table.isShowCardCallBanker()) {
      if (!CheckUtil.checkBetween(niu, 0, 10)) {
        LOG.error("[{}]玩家[{}][{}]托管，下注选项niu值有误,为[{}]", this.game.getDesc(),
            player.getPlayerName(), player.getId(), niu);
        return false;
      }
    } else {
      if (niu != 0) {
        LOG.error("[{}]玩家[{}][{}]托管，下注选项niu为不0,为[{}]", this.game.getDesc(),
            player.getPlayerName(), player.getId(), niu);
        return false;
      }
    }
    if (!betOptionPo.getBetChips().contains(betOption.getChips())) {
      LOG.error("[{}]玩家[{}][{}]托管，下注选项筹码有误,为", this.game.getDesc(), player.getPlayerName(),
          player.getId(), betOption.getChips());
      return false;
    }
    return true;
  }

  /**
   * @Title offTrustee.
   * @Description 取消托管
   * @date 2018年6月8日 上午11:03:09
   * @param player .
   * @return .
   */
  public ResMessage offTrustee(Player player) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中,不能取消托管", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    MustNiuNiuTable table = seat.table();

    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]为旁观者,不能取消托管", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }
    // 移除托管
    table.getTrustees().remove(player.getId());
    seat.setSysHost(false);
    // 发送托管变化消息
    msgMgr.sendTrusteeChangeMsg(seat, false);
    return ResMessage.DEFAULT;
  }

  private void doAnswerApplyCloseRoom(MustNiuNiuSeat seat, boolean agree) {
    seat.setAnswerCloseRoom(agree);
    stepFutureDisable(seat.stepFuture);
    MustNiuNiuTable table = seat.table();
    MustNiuNiuRoom room = table.room();

    // 如果同意
    if (agree) {
      if (answerOver(table)) { // 全部同意
        msgMgr.sendCloseRoomCardMsg(table, CloseType.STARTED_BY_PLAYERS);
        // 游戏结束
        gameOver(room);
      }
    } else {
      // 取消定时不同意
      cancelDisagreeCloseRoomSchedule(table);
    }
    msgMgr.sendAnswerApplyCloseRoomMsg(seat);
  }

  /**
   * @Title answerOver.
   * @Description 判断是否一全部应答完
   * @date 2018年6月8日 下午4:25:51
   * @param table .
   * @return .
   */
  private boolean answerOver(MustNiuNiuTable table) {
    boolean result = true;
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState().equals(SeatState.GAMING)) {
        if (s.getAnswerCloseRoom() == null || s.getAnswerCloseRoom() == false) {
          result = false;
        }
      }
    }
    return result;
  }

  private BetTypeBo checkBetChips(MustNiuNiuSeat seat, long chips) {
    MustNiuNiuTable table = seat.table();
    // 基础下注选项
    MustNiuNiuBetOptionPo basicBetOption = table.findBetOptionPo();

    // 最高抢庄倍数
    int maxCallDealerMultiple = 1;
    if (table.isShowCardCallBanker()) { // 如果是明牌抢庄，最高抢庄倍数为设置的值
      maxCallDealerMultiple = table.getCallDealerMultiple();
    }

    List<Long> betOptions = new ArrayList<Long>();
    // 下注限制
    MustNiuNiuAdvanceOptionPo betLimit =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_LIMIT);

    // 如果含有下注限制
    if (betLimit != null) {
      // 最低下注筹码
      int minBet = basicBetOption.findMinBetChip();
      if (seat.getCallDealerMultiple() == maxCallDealerMultiple) { // 最高倍抢庄失败的玩家，不能下最低分
        for (Integer bet : basicBetOption.getBetChips()) {
          if (bet != minBet) {
            betOptions.add((long) bet);
          }
        }
      } else {
        for (Integer bet : basicBetOption.getBetChips()) {
          betOptions.add((long) bet);
        }
      }
    } else {
      for (Integer bet : basicBetOption.getBetChips()) {
        betOptions.add((long) bet);
      }
    }

    BetTypeBo result = BetTypeBo.ERROR;
    // 如果已经加倍
    if (seat.isDoubleBet()) {
      List<Long> totalOptions = new ArrayList<Long>();
      for (Long bet : betOptions) {
        if (!totalOptions.contains(bet * 2)) {
          totalOptions.add(bet * 2);
        }
      }
      betOptions.clear();
      betOptions.addAll(totalOptions);
      if (betOptions.contains(chips)) {
        result = BetTypeBo.DOUBLE_BASE_BET; // 底分加倍
      }
    } else {
      if (betOptions.contains(chips)) {
        result = BetTypeBo.BASE_BET; // 底分不加倍
      }
    }

    if (result != BetTypeBo.ERROR) {
      return result;
    } else {
      LOG.info("1. 下注[{}]不属于基础下注选项及加倍,下注选项为[{}],加倍[{}]", chips, JsonUtil.toJson(betOptions),
          seat.isDoubleBet());
    }

    // 含有推注情况下，区分加倍与不加倍
    if (seat.getPreWinBetSelf() > 0) {
      if (!seat.isDoubleBet()) { // 不加倍,直接使用推注
        if (!betOptions.contains(seat.getPreWinBetSelf())) {
          betOptions.add(seat.getPreWinBetSelf());
        }
        if (betOptions.contains(chips)) {
          result = BetTypeBo.EXTR_BET; // 推注不加倍
        }
      } else { // 加倍，计算推注
        // 最低下注选项
        Integer minBetOption = table.findBetOptionPo().findMinBetChip();
        // 最高推注倍数
        MustNiuNiuExtrBetOptionPo extBetOption = table.findExtrBetOptionPo();

        int maxExtrBetChips = extBetOption.getMultiple() * minBetOption;
        long doublePreWinBet = seat.getPreWinBetSelf() * 2;
        if (doublePreWinBet > maxExtrBetChips) {
          doublePreWinBet = maxExtrBetChips;
        }
        if (!betOptions.contains(doublePreWinBet)) {
          betOptions.add(doublePreWinBet);
        }
        if (betOptions.contains(chips)) {
          result = BetTypeBo.DOUBLE_EXTR_BET; // 推注不加倍
        }
      }
      if (result == BetTypeBo.ERROR) {
        LOG.error("2. 下注[{}]不合法,合法下注为[{}],未加倍推注[{}], 加倍[{}]", chips, JsonUtil.toJson(betOptions),
            seat.getPreWinBetSelf(), seat.isDoubleBet());
      }
    }

    return result;
  }

  private boolean checkCallBankerMultiple(MustNiuNiuTable table, int multiple) {
    MustNiuNiuRoom room = table.room();
    // 如果是自由抢庄,multiple只能为0|1
    if (isFreeCallBanker(room.getRoomCfg().cfgs().getType())) {
      return CheckUtil.checkBetween(multiple, 0, 1);
    }
    if (table.isShowCardCallBanker()) {
      int maxMultiple = room.getRoomCfg().cfgs().getCallBankerMultiple().getMultiple();
      return CheckUtil.checkBetween(multiple, 0, maxMultiple);
    }
    return false;
  }

  private boolean stepFutureDisable(ScheduledFuture<?> stepFuture) {
    if (stepFuture != null) {
      if (stepFuture.isCancelled() || stepFuture.isDone()) {
        return true;
      } else {
        return stepFuture.cancel(false);
      }
    }
    return true;
  }

  private void delayStartGame(MustNiuNiuTable table, int delay, TimeUnit unit) {
    if (stepFutureDisable(table.stepFuture)) {
      table.stepFuture = this.scheduleMgr.schedule(() -> {
        // 发送开桌信息
        msgMgr.sendGameStartMsg(table);
        // 一局开始
        roundStart(table.room());
        // 进入阶段
        if (table.isNiuNiuBeBanker() || table.isOwnerBeBanker()) {
          doBetStage(table);
        } else if (table.isFreeCallBanker() || table.isShowCardCallBanker()) {
          // 游戏进入叫庄状态
          doCallBankerStage(table);
        }
      }, delay, unit, this.gameExecutor);
    }
  }

  /**
   * @Title doReadyStage.
   * @Description 玩家进入准备阶段
   * @date 2018年6月2日 上午11:10:34
   * @param table .
   */
  private void doReadyStage(MustNiuNiuTable table) {
    // 游戏处于准备阶段
    table.setStatus(MustNiuNiuGameState.READY);
    // 重置桌子
    table.reset();
    // 重置座位
    resetTableSeat(table);
    // 一局开始
    roundStart(table.room());

    int readyTime = MustNiuNiuStageTimeConstant.READY_TIME;
    msgMgr.sendGameStage(table, readyTime);

    // 托管准备
    trusteeReady(table);

    // 进入下一个阶段(叫庄)
    if (stepFutureDisable(table.stepFuture)) {
      table.stepFuture = scheduleMgr.schedule(() -> {
        // 超时准备
        for (MustNiuNiuSeat s : table.getGameSeats(true)) {
          if (s.getState() == SeatState.SEATED) {
            // 超时准备
            doReady(s);
          }
        }
      }, readyTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);

      LOG.info("[{}]房间[{}]桌子[{}]阶段[{}]", this.game.getDesc(), table.room().getId(), table.getId(),
          table.getStatus().desc);
    }
  }

  /**
   * @Title doCallBanker.
   * @Description 抢庄阶段
   * @date 2018年6月1日 下午8:40:59
   * @param table .
   */
  private void doCallBankerStage(MustNiuNiuTable table) {
    // 座位状态由准备变为游戏中
    seatStateToGaming(table);
    // 桌子状态为叫庄阶段
    table.setStatus(MustNiuNiuGameState.CALL_BANKER);
    // 如果是明牌抢庄,首先发四张牌
    if (table.isShowCardCallBanker()) {
      niuniuControl.dealSccbFirstFourCards(table);
    }

    // 阶段信息变化
    int stageTime = MustNiuNiuStageTimeConstant.CALL_BANKER_TIME;
    msgMgr.sendGameStage(table, stageTime);


    // 托管叫庄
    trusteeCallDealer(table);

    // 进入下一个阶段(下注)
    if (stepFutureDisable(table.stepFuture)) {
      table.stepFuture = scheduleMgr.schedule(() -> {
        // 超时业务处理,准备了但尚未叫庄的玩家叫庄
        for (MustNiuNiuSeat s : table.getGameSeats(true)) {
          if (s.getState() == SeatState.GAMING && s.getCallDealerMultiple() == null) {
            doCallDealer(s, 0); // 超时不抢庄
          }
        }
        // doBetStage(table);
      }, stageTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);

      LOG.info("[{}]房间[{}]桌子[{}]阶段[{}]", this.game.getDesc(), table.room().getId(), table.getId(),
          table.getStatus().desc);
    }
  }

  private void trusteeReady(MustNiuNiuTable table) {
    if (stepFutureDisable(table.trusteeFuture)) {
      LOG.info("[{}]房间[{}]桌子[{}]托管准备", this.game.getDesc(), table.room().getId(), table.getId());
      table.trusteeFuture = scheduleMgr.schedule(() -> {
        LOG.info("[{}]房间[{}]桌子[{}]执行托管准备", this.game.getDesc(), table.room().getId(),
            table.getId());
        // 托管玩家
        Map<Long, TrusteeBo> trusteeMap = table.getTrustees();
        for (Long playerId : trusteeMap.keySet()) {
          // 获取玩家座位
          MustNiuNiuSeat seat = super.findPlayerSeat(playerId);
          if (seat.getState().equals(SeatState.SEATED)) {
            doReady(seat);
          }
        }
      }, 1, TimeUnit.SECONDS, gameExecutor);
    }
  }

  private void trusteeShowdown(MustNiuNiuTable table) {
    if (stepFutureDisable(table.trusteeFuture)) {
      LOG.info("[{}]房间[{}]桌子[{}]托管摊牌", this.game.getDesc(), table.room().getId(), table.getId());
      table.trusteeFuture = scheduleMgr.schedule(() -> {
        LOG.info("[{}]房间[{}]桌子[{}]执行托管摊牌", this.game.getDesc(), table.room().getId(),
            table.getId());
        // 托管玩家
        Map<Long, TrusteeBo> trusteeMap = table.getTrustees();
        for (Long playerId : trusteeMap.keySet()) {
          // 获取玩家座位
          MustNiuNiuSeat seat = super.findPlayerSeat(playerId);
          if (seat.getState().equals(SeatState.GAMING) && !seat.isShowdown()) {
            doShowdown(seat);
          }
        }
      }, 1, TimeUnit.SECONDS, gameExecutor);
    }
  }

  private void trusteeCallDealerSeat(MustNiuNiuSeat seat, TrusteeBo trustee) {
    // 叫庄选项
    TrusteeCallBankerOptionBo callBankerOption = trustee.getCallBankerOption();
    Integer callDealerMultiple = seat.getCallDealerMultiple();
    // 已经叫庄的跳过
    if (callDealerMultiple != null) {
      return;
    }
    MustNiuNiuTable table = seat.table();
    // 自由抢庄
    if (table.isFreeCallBanker()) {
      if (callBankerOption.getNiu() == -1) { // -1:不抢庄
        doCallDealer(seat, 0);
      } else {
        doCallDealer(seat, 1);
      }
    } else if (table.isShowCardCallBanker()) { // 如果是明牌抢庄
      if (callBankerOption.getNiu() == -1) { // -1:不抢庄
        doCallDealer(seat, 0);
      } else {
        if (cardsMatchNiu(seat.getCards(), table.getCardTypeMap(), callBankerOption.getNiu())) {
          doCallDealer(seat, callBankerOption.getMultiple());
        } else {
          doCallDealer(seat, 0);
        }
      }
    }
  }

  private void trusteeCallDealer(MustNiuNiuTable table) {
    if (stepFutureDisable(table.trusteeFuture)) {
      LOG.info("[{}]房间[{}]桌子[{}]托管叫庄", this.game.getDesc(), table.room().getId(), table.getId());
      table.trusteeFuture = scheduleMgr.schedule(() -> {
        LOG.info("[{}]房间[{}]桌子[{}]执行托管叫庄", this.game.getDesc(), table.room().getId(),
            table.getId());
        // 托管玩家
        Map<Long, TrusteeBo> trusteeMap = table.getTrustees();
        for (Long playerId : trusteeMap.keySet()) {
          TrusteeBo trustee = trusteeMap.get(playerId);
          // 获取玩家座位
          MustNiuNiuSeat seat = super.findPlayerSeat(playerId);
          trusteeCallDealerSeat(seat, trustee);
        }
      }, 1, TimeUnit.SECONDS, gameExecutor);
    }
  }

  private void trusteeBetSeat(MustNiuNiuSeat seat, TrusteeBo trustee) {
    if (seat.isDealer()) { // 庄家不下注
      return;
    }
    MustNiuNiuTable table = seat.table();
    // 是否开启买码
    MustNiuNiuAdvanceOptionPo betOtherOption =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_OTHER);
    // 如果开启买码，但玩家未买码,不买码
    if (betOtherOption != null && seat.getBetOtherPlayerId() == null) {
      // 设置为不买码
      doBetOther(seat, 0L, 0L);
    }
    // 如果已经下注，跳过
    if (seat.getBetedchips() > 0L) {
      return;
    }
    // 可推注金额
    long extrBetChips = seat.getPreWinBetSelf();
    if (extrBetChips > 0L) {
      int niu = trustee.getExtrBetOption().getNiu();
      if (niu == 0) { // 任意牌推注
        doBet(seat, extrBetChips);
        return;
      } else if (table.isShowCardCallBanker()
          && cardsMatchNiu(seat.getCards(), table.getCardTypeMap(), niu)) { // 满足牛几以上开始推注
        doBet(seat, extrBetChips);
        return;
      }
    }

    // 最高抢庄倍数
    int maxCallDealerMultiple = 1;
    if (table.isShowCardCallBanker()) { // 如果是明牌抢庄，最高抢庄倍数为设置的值
      maxCallDealerMultiple = table.getCallDealerMultiple();
    }

    MustNiuNiuBetOptionPo basicBetOption = table.findBetOptionPo();
    // 下注限制
    MustNiuNiuAdvanceOptionPo betLimit =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_LIMIT);
    // 可下注选项
    List<Integer> betOptions = new ArrayList<Integer>(basicBetOption.getBetChips());

    // 如果含有下注限制
    if (betLimit != null) {
      // 最低下注筹码
      Integer minBet = basicBetOption.findMinBetChip();
      if (seat.getCallDealerMultiple() == maxCallDealerMultiple) {
        // 最高倍抢庄失败的玩家，不能下最低分
        betOptions.remove(minBet);
      }
    }
    Collections.sort(betOptions);

    // 下注选项
    TrusteeBetOptionBo betOption = trustee.getBetOption();
    int niu = betOption.getNiu();
    int betChips = betOption.getChips();
    if (niu == 0) { // 任意牌
      if (!betOptions.contains(betChips)) {
        if (betChips != basicBetOption.findMinBetChip()) {
          LOG.warn("托管金额有误,下最低选项");
          betChips = betOptions.get(0);
        }
      }
    } else {
      if (table.isShowCardCallBanker()) {
        if (cardsMatchNiu(seat.getCards(), table.getCardTypeMap(), niu)) {
          if (!betOptions.contains(betChips)) {
            LOG.warn("明牌抢庄托管金额有误,下最低选项");
            betChips = betOptions.get(0);
          }
        } else {
          betChips = betOptions.get(0);
        }
      } else {
        LOG.warn("非明牌抢庄niu不能为非0,下最低选项");
        betChips = betOptions.get(0);
      }
    }
    doBet(seat, betChips);
  }

  private void trusteeBet(MustNiuNiuTable table) {
    if (stepFutureDisable(table.trusteeFuture)) {
      LOG.info("[{}]房间[{}]桌子[{}]托管下注", this.game.getDesc(), table.room().getId(), table.getId());
      table.trusteeFuture = scheduleMgr.schedule(() -> {
        LOG.info("[{}]房间[{}]桌子[{}]执行托管下注", this.game.getDesc(), table.room().getId(),
            table.getId());
        // 托管玩家
        Map<Long, TrusteeBo> trusteeMap = table.getTrustees();
        for (Long playerId : trusteeMap.keySet()) {
          TrusteeBo trustee = trusteeMap.get(playerId);
          // 获取玩家座位
          MustNiuNiuSeat seat = super.findPlayerSeat(playerId);
          trusteeBetSeat(seat, trustee);
        }
      }, 1, TimeUnit.SECONDS, gameExecutor);
    }
  }

  /**
   * @Title cardsMatchNiu.
   * @Description 判断四张牌是否满足倍数,用于托管时叫庄和下注使用
   * @author houdongsheng
   * @date 2018年6月9日 上午9:37:01
   * @param cards 四张牌
   * @param cardTypeMaps 支持的牌型
   * @param multiple 需要满足的倍数
   * @return 满足返回true,否则返回false
   */
  private boolean cardsMatchNiu(List<NiuNiuCard> cards,
      Map<MustNiuNiuCardType, Integer> cardTypeMaps, int niu) {
    // 实现功能
    List<MustNiuNiuCardType> cardTypeList = new ArrayList<>(cardTypeMaps.keySet());
    MustNiuNiuCardType cardType =
        MustNiuNiuTrusteeCardsTypeGetter.getCardsType(cards, cardTypeList);
    if (cardType.niu >= niu) {
      return true;
    }
    return false;
  }

  /**
   * @Title doBetStage.
   * @Description 下注
   * @date 2018年6月2日 下午3:16:58
   * @param table .
   */
  private void doBetStage(MustNiuNiuTable table) {
    if (table.isNiuNiuBeBanker()) { // 牛牛玩家中产生庄家
      List<Long> niuNiuCardTypePlayers = table.getNiuNiuCardTypePlayers();
      // 座位状态由准备变为游戏中
      seatStateToGaming(table);
      long dealerId = 0L;
      if (EmptyUtil.listIsNotEmpty(niuNiuCardTypePlayers)) {
        dealerId = RandCodeUtil.randomList(niuNiuCardTypePlayers);
      } else if (table.getPreDealerId() != null && table.getPreDealerId() > 0L) {
        dealerId = table.getPreDealerId();
      } else {
        List<MustNiuNiuSeat> seats = new ArrayList<>();
        for (MustNiuNiuSeat s : table.getGameSeats(true)) {
          if (s.getState().equals(SeatState.GAMING)) {
            seats.add(s);
          }
        }
        MustNiuNiuSeat dealerSeat = RandCodeUtil.randomList(seats);
        dealerId = dealerSeat.getPlayerId();
      }
      MustNiuNiuSeat dealerSeat = super.findPlayerSeat(dealerId);
      seatBeDealer(dealerSeat);
    } else if (table.isOwnerBeBanker()) { // 固定庄家坐庄
      // 座位状态由准备变为游戏中
      seatStateToGaming(table);

      Long ownerId = table.room().getOwnerId();
      MustNiuNiuSeat dealerSeat = super.findPlayerSeat(ownerId);
      seatBeDealer(dealerSeat);
    }
    table.setStatus(MustNiuNiuGameState.BET);

    // 阶段信息变化
    int stageTime = MustNiuNiuStageTimeConstant.BET_TIME;
    msgMgr.sendGameStage(table, stageTime);

    MustNiuNiuAdvanceOptionPo betOther =
        table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_OTHER);

    // 托管下注
    trusteeBet(table);

    // 进入下一个阶段(下注)
    if (stepFutureDisable(table.stepFuture)) {
      table.stepFuture = scheduleMgr.schedule(() -> {
        for (MustNiuNiuSeat s : table.getGameSeats(true)) {
          // 便利所有闲家
          if (s.getState() == SeatState.GAMING && !s.isDealer()) {
            // 可买码，超时不买码
            if (betOther != null && s.getBetOtherPlayerId() == null) {
              // 设置为不买码
              doBetOther(s, 0L, 0L);
            }
            // 已经下注，跳过
            if (s.getBetedchips() > 0L) {
              continue;
            }

            MustNiuNiuAdvanceOptionPo betLimit =
                table.findAdvanceOptionPo(MustNiuNiuAdvanceOption.BET_LIMIT);
            MustNiuNiuBetOptionPo betOption = table.findBetOptionPo();
            long chips = betOption.findMinBetChip();
            if (betLimit != null) {
              // 最高倍数叫庄失败的玩家不能下最低分
              if (s.getCallDealerMultiple() == table.dealer().getCallDealerMultiple()) {
                // 筛选出倒数第二低分
                int secondMin = 0;
                for (Integer bet : betOption.getBetChips()) {
                  if (bet > chips) {
                    if (secondMin == 0) {
                      secondMin = bet;
                    } else {
                      if (secondMin > bet) {
                        secondMin = bet;
                      }
                    }
                  }
                }
                chips = secondMin;
              }
            }
            doBet(s, chips);
          }
        }
        // doSendOpenCardStage(table);
      }, stageTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);

      LOG.info("[{}]房间[{}]桌子[{}]阶段[{}]", this.game.getDesc(), table.room().getId(), table.getId(),
          table.getStatus().desc);
    }
  }

  private void doSendOpenCardStage(MustNiuNiuTable table) {
    table.setStatus(MustNiuNiuGameState.OPEN_CARD); // 开牌
    // 阶段信息变化
    int stageTime = MustNiuNiuStageTimeConstant.OPEN_CARD_TIME;
    msgMgr.sendGameStage(table, stageTime);
    // 如果是明牌抢庄
    if (table.isShowCardCallBanker()) {
      LOG.info("doSendOpenCardStage=====>>>>>>>>>>>>>>>>>>");
      for (MustNiuNiuSeat seat : table.getGameSeats(true)) {
        LOG.info("玩家[{}]牌[{}]", seat.getPlayerId(), JsonUtil.toJson(seat.getCards()));
      }
    }
    // 发牌和结算
    niuniuControl.controlDealCards(table);

    trusteeShowdown(table);

    // 进入下一个阶段(下注)
    if (stepFutureDisable(table.stepFuture)) {
      table.stepFuture = scheduleMgr.schedule(() -> {
        // 所有未摊牌的闲家摊牌
        for (MustNiuNiuSeat s : table.getGameSeats(true)) {
          // 便利所有闲家
          if (s.getState() == SeatState.GAMING && !s.isShowdown()) {
            doShowdown(s);
          }
        }
      }, stageTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);

      LOG.info("[{}]房间[{}]桌子[{}]阶段[{}]", this.game.getDesc(), table.room().getId(), table.getId(),
          table.getStatus().desc);
    }
  }

  private void doShowResultStage(MustNiuNiuTable table) {
    table.setStatus(MustNiuNiuGameState.SHOW_RESULT); // 展示结果
    // 阶段信息变化
    int stageTime = MustNiuNiuStageTimeConstant.SHOW_RESULT_TIME;
    msgMgr.sendGameStage(table, stageTime);
    // 发送结算信息
    List<RoundBillInfo> billInfos = new ArrayList<>();
    for (MustNiuNiuSeat seat : table.getGameSeats(true)) {
      if (seat.getState().equals(SeatState.GAMING)) {
        long playerId = seat.getPlayerId();
        RoundBillInfo billInfo = new RoundBillInfo();
        billInfo.setOrder(seat.getOrder());
        billInfo.setChips(seat.getBillChips());
        billInfo.setPlayerId(playerId);
        billInfo.setPreWinBetSelf(seat.getPreWinBetSelf() != null ? seat.getPreWinBetSelf() : 0L);
        billInfos.add(billInfo);

        Player player = playerMgr.getPlayer(playerId);
        // 筹码记录
        DbLogService.log(new RoomCardMustNiuNiuWinLoseLog(player, this.game));
        // 发送筹码变化消息
        msgMgr.sendChipsChangeMsg(seat);
      }
    }
    msgMgr.sendRoundOver(table, billInfos);
    LOG.info("结算信息: [{}]", JsonUtil.toJson(billInfos));

    // 一局结束
    boolean isGameOver = roundEnd(table.room());
    // 如果是固定庄家(庄家把分数都输光,游戏结束)
    if (table.isOwnerBeBanker()) {
      MustNiuNiuBankerChipPo bankerChips = table.findBankerChipPo();
      if (bankerChips.getChips() > 0) {
        MustNiuNiuSeat dealer = table.dealer();
        if (dealer.getTotalChips() <= 0L) {
          isGameOver = true;
          // 发送破产通知
          msgMgr.sendCloseRoomCardMsg(table, CloseType.BANKER_BANKRUPT);
        }
      }
    }
    if (!isGameOver) {
      // 进入下一个阶段(下注)
      if (stepFutureDisable(table.stepFuture)) {
        table.stepFuture = scheduleMgr.schedule(() -> {
          // 进入下一局之前，做最后后的处理
          saveRoundInfo(table);

          table.reset();
          // 进入下一个阶段
          doReadyStage(table);
        }, stageTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);
        LOG.info("[{}]房间[{}]桌子[{}]阶段[{}]", this.game.getDesc(), table.room().getId(),
            table.getId(), table.getStatus().desc);
      }
    } else { // 游戏已经结束
      MustNiuNiuRoom room = table.room();

      // 保存游戏信息
      gameOver(room);
    }
  }

  // 保存本局信息
  private void saveRoundInfo(MustNiuNiuTable table) {
    // 游戏尚未发牌，不保存数据
    if (table.getStatus().equals(MustNiuNiuGameState.NO_START)
        || table.getStatus().equals(MustNiuNiuGameState.READY)
        || table.getStatus().equals(MustNiuNiuGameState.CALL_BANKER)
        || table.getStatus().equals(MustNiuNiuGameState.BET)) {
      return;
    }
    for (MustNiuNiuSeat seat : table.getGameSeats(true)) {
      if (seat.getState().equals(SeatState.GAMING)
          && (table.getStatus().equals(MustNiuNiuGameState.OPEN_CARD)
              || table.getStatus().equals(MustNiuNiuGameState.SHOW_RESULT))) {
        RoomCardNiuniuScoreDetailsDomain detailDomain = new RoomCardNiuniuScoreDetailsDomain();
        Player player = playerMgr.getPlayer(seat.getPlayerId());
        // 玩家局数增加
        player.setRoundCount(player.getRoundCount() + 1);

        detailDomain.setRoom(table.room().getId());
        detailDomain.setPlayerId(seat.getPlayerId());
        detailDomain.setSuperId(player.getSuperId());
        detailDomain.setPlayerName(player.getPlayerName());
        detailDomain.setRound(table.room().getRoomCfg().getPlayGames());
        detailDomain.setBet(seat.getBetedchips());
        detailDomain.setScore(seat.getBillChips());
        detailDomain.setTotalScore(seat.getTotalChips());
        detailDomain.setBetOtherChips(seat.getBetOtherBetChips());
        detailDomain.setBetOtherPlayerId(
            seat.getBetOtherPlayerId() == null ? 0L : seat.getBetOtherPlayerId());
        if (seat.getBetOtherPlayerId() != null && seat.getBetOtherPlayerId() > 0) {
          detailDomain.setBetOtherPlayerName(
              playerMgr.getPlayer(seat.getBetOtherPlayerId()).getPlayerName());
        } else {
          detailDomain.setBetOtherPlayerName("");
        }
        detailDomain.setCardType((byte) seat.getCardsType().niu);
        List<Integer> cards = new ArrayList<Integer>();
        for (NiuNiuCard item : seat.getBestCards()) {
          cards.add(item.ordinal());
        }
        detailDomain.setCardValues(JsonUtil.toJson(cards));
        detailDomain.setIsBanker(seat.isDealer());
        detailDomain.setCallBankerMultiple(
            seat.getCallDealerMultiple() == null ? 0 : seat.getCallDealerMultiple());

        table.getScoreDetails().add(detailDomain);
      }
    }
  }

  @Override
  public void gameOver(MustNiuNiuRoom room) {
    // 发送游戏结束信息
    MustNiuNiuTable table = room.table();
    List<GameBillInfo> gameBillInfos = new ArrayList<>();
    if (!table.getStatus().equals(MustNiuNiuGameState.NO_START)) {
      for (MustNiuNiuSeat seat : table.getGameSeats(true)) {
        stepFutureDisable(seat.stepFuture); // 关闭房间定时器
        if (seat.getState().equals(SeatState.GAMING)) {
          long playerId = seat.getPlayerId();
          GameBillInfo billInfo = new GameBillInfo();
          billInfo.setOrder(seat.getOrder());
          billInfo.setChips(seat.getTotalChips());
          billInfo.setPlayerId(playerId);
          gameBillInfos.add(billInfo);
        }
      }

      // 保存本局信息
      saveRoundInfo(table);

      // 如果游戏在未发牌，本局撤销
      if (table.getStatus().equals(MustNiuNiuGameState.READY)
          || table.getStatus().equals(MustNiuNiuGameState.CALL_BANKER)
          || table.getStatus().equals(MustNiuNiuGameState.BET)) {
        MustNiuNiuRoomCardRoomCfg roomCfg = room.getRoomCfg();
        roomCfg.setLeftGame(roomCfg.getLeftGame() + 1);
        roomCfg.setPlayGames(roomCfg.getPlayGames() - 1);
      }
    }

    // 减少群中的房间数量
    Integer groupId = room.getRoomCfg().getGroupId();
    if (groupId != null && groupId > 0) {
      groupDao.decGroupRoomNum(groupId, 1);
    }

    // 关闭主流程事件
    stepFutureDisable(table.stepFuture);
    // 关闭托管事件
    stepFutureDisable(table.trusteeFuture);
    // 关闭房间中座位上的事件
    for (MustNiuNiuSeat seat : table.seats()) {
      if (seat.getPlayerId() > 0L) {
        stepFutureDisable(seat.stepFuture);
      }
    }

    if (EmptyUtil.listIsNotEmpty(table.getScoreDetails())) {
      roomCardNiuniuScoreDetailsDao.insertBatch(table.getScoreDetails());
    }

    // 保存战绩
    List<RoomCardScoreDomain> socreDomains = toScoreDomains(table);
    if (EmptyUtil.listIsNotEmpty(socreDomains)) {
      roomCardScoreMapper.insertBatch(socreDomains);
    }
    super.gameOver(room);

    msgMgr.sendGameOver(table, gameBillInfos);
  }

  // private void storePlayerRoundCount(MustNiuNiuTable table) {
  // Map<Long, Integer> playerRoundMap = new HashMap<>();
  // if (EmptyUtil.listIsNotEmpty(table.getScoreDetails())) {
  // for (RoomCardNiuniuScoreDetailsDomain item : table.getScoreDetails()) {
  // playerRoundMap.put(item.getPlayerId(), playerRoundMap.get(item.getPlayerId()) + 1);
  // }
  // }
  // // 更新到数据库
  // if (playerRoundMap.size() > 0) {
  // for (Long playerId : playerRoundMap.keySet()) {
  // playerRoundCountDao.incRoundCount(playerId, playerRoundMap.get(playerId));
  // }
  // }
  // }

  private List<RoomCardScoreDomain> toScoreDomains(MustNiuNiuTable table) {
    long bankerChips = 0L;
    boolean isOwnerBeBanker = false;
    if (table.isOwnerBeBanker()) {
      isOwnerBeBanker = true;
      bankerChips = table.findBankerChipPo().getChips();
    }

    List<RoomCardScoreDomain> totalScores = new ArrayList<RoomCardScoreDomain>();
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState().equals(SeatState.GAMING)) {
        RoomCardScoreDomain domain = new RoomCardScoreDomain();
        domain.setGame(table.room().game().getType());
        domain.setRoom(table.room().getId());
        domain.setGroupId(table.room().getGroupId());
        PlayerBo player = playerMgr.selectPlayer(s.getPlayerId());
        domain.setPlayerId(player.getId());
        domain.setSuperId(player.getSuperId());
        domain.setPlayerName(player.getPlayerName());
        domain.setPlayerAvatar(player.getPlayerAvatar());
        if (isOwnerBeBanker && table.room().getOwnerId().equals(s.getPlayerId())) {
          domain.setWin(s.getTotalChips() - bankerChips);
        } else {
          domain.setWin(s.getTotalChips());
        }
        totalScores.add(domain);
      }
    }
    return totalScores;
  }

  private void resetTableSeat(MustNiuNiuTable table) {
    List<MustNiuNiuSeat> seats = table.getGameSeats(true);
    for (MustNiuNiuSeat seat : seats) {
      seat.resetButTotalChips();
    }
  }

  /**
   * @Title seatStateToGaming.
   * @Description 作为状态准备变为游戏中
   * @date 2018年6月1日 下午9:07:00
   * @param table .
   */
  private void seatStateToGaming(MustNiuNiuTable table) {
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getState() == SeatState.READY) {
        s.setState(SeatState.GAMING);
      }
    }
  }

  /**
   * @Title readiedOver.
   * @Description 判断是否所有人都准备好
   * @date 2018年6月1日 下午8:29:06
   * @param table .
   * @return .
   */
  private boolean readiedOver(MustNiuNiuTable table) {
    boolean tableReadied = true;
    // 准备好的玩家
    int readiedNum = 0;
    for (MustNiuNiuSeat s : table.getGameSeats(true)) {
      if (s.getPlayerId() > 0) {
        if (!s.readied()) {
          tableReadied = false;
          break;
        }
        readiedNum++;
      }
    }
    if (readiedNum <= 1) { // 必须大于1人才算准备好
      tableReadied = false;
    }
    table.setReadied(tableReadied);
    return tableReadied;
  }


  private MustNiuNiuSeat doSeatDown(Player player, SeatState state) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    HuohuaAssert.isTrue(seat != null, ErrorCode.ROOMCARD_NOT_IN);
    HuohuaAssert.isTrue(seat.isByStander(), ErrorCode.ROOMCARD_ALREADY_SEAT_DOWN);

    MustNiuNiuSeat gameSeat = findEmptySeat(player, (MustNiuNiuRoom) player.curRoom, false);
    HuohuaAssert.isTrue(gameSeat != null && !gameSeat.isByStander(),
        ErrorCode.ROOMCARD_NOT_ENOUGH_SEAT);
    gameSeat.setPlayerId(seat.getPlayerId());
    gameSeat.setPlayerName(seat.getPlayerName());
    gameSeat.setSex(seat.getSex());
    gameSeat.setRobot(false);
    gameSeat.setTotalChips(seat.getTotalChips());
    gameSeat.setState(state);

    // 清空旁观者座位
    seat.clear();
    // 坐下人数加1
    gameSeat.table().room().getSeatDownNum().incrementAndGet();

    //
    super.updatePlayerSeat(player.getId(), gameSeat);
    player.curSeat = gameSeat;

    msgMgr.sendEnterRoomCardMsg(player);

    return gameSeat;
  }

  /**
   * @Title sendMsg.
   * @Description 玩家聊天
   * @date 2018年6月8日 上午11:04:25
   * @param player 玩家编号
   * @param otherPlayerId 接收玩家编号(可发自己,0:群发)
   * @param msgType 消息类型(客户端自己定义)
   * @param msg 消息内容
   * @return .
   */
  public ResMessage sendMsg(Player player, long otherPlayerId, int msgType, String msg) {
    MustNiuNiuSeat seat = super.findPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[{}]玩家[{}][{}]不在牌桌中不能发送聊天", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      return ResMessage.DEFAULT;
    }

    if (seat.isByStander()) {
      LOG.error("[{}]玩家[{}][{}]盘观者不能聊天", this.game.getDesc(), player.getPlayerName(),
          player.getId());
      HuohuaAssert.isTrue(false);
    }

    MustNiuNiuSeat otherSeat = null;
    if (otherPlayerId > 0) {
      otherSeat = super.findPlayerSeat(otherPlayerId);
      if (otherSeat == null) {
        LOG.error("[{}]玩家[{}][{}]接收消息玩家有误", this.game.getDesc(), player.getPlayerName(),
            player.getId());
        HuohuaAssert.isTrue(false);
      }
    }

    int otherOrder = 0;
    if (otherSeat != null) {
      otherOrder = otherSeat.getOrder();
    }

    msgMgr.sendMsgMsg(player, otherPlayerId, otherOrder, msgType, msg);
    return ResMessage.DEFAULT;
  }

  @Override
  protected MustNiuNiuRoom roomCfgToRoom(MustNiuNiuRoomCardRoomCfg room) {
    if (room == null) {
      LOG.error("房卡转换失败，房卡配置有误null: [{}]", room);
      HuohuaAssert.isTrue(false);
    }
    MustNiuNiuRoomCardRoomCfgValue cfgValue = room.cfgs();
    if (cfgValue == null) {
      LOG.error("房卡转换失败，房卡详细配置配置有误null: [{}]", room);
      HuohuaAssert.isTrue(false);
    }
    MustNiuNiuSeatPo seat = cfgValue.getSeat();

    if (seat == null) {
      LOG.error("房卡转换失败，房卡配置信息有误，房间座位数有误: [{}]", room);
      HuohuaAssert.isTrue(false);
    }

    return new MustNiuNiuRoom(room, seat.getSeat());
  }

  @Override
  protected void unStartExpire(MustNiuNiuRoom room) {
    // 退还款项
    returnBackGold(room.table());
    msgMgr.sendCloseRoomCardMsg(room.table(), CloseType.UNSTART_TIME_OUT);
  }

  // ==
  private MustNiuNiuBetOptionPo findBetOption(List<MustNiuNiuBetOptionPo> list, int id) {
    MustNiuNiuBetOptionPo result = null;
    for (MustNiuNiuBetOptionPo item : list) {
      if (item.getId() == id) {
        result = item;
        break;
      }
    }
    return result;
  }

  private MustNiuNiuSeatPo findSeatPo(List<MustNiuNiuSeatPo> list, int seat) {
    MustNiuNiuSeatPo result = null;
    for (MustNiuNiuSeatPo item : list) {
      if (item.getSeat() == seat) {
        result = item;
        break;
      }
    }
    return result;
  }

  private MustNiuNiuRoundPo findRoundPo(List<MustNiuNiuRoundPo> list, int round) {
    MustNiuNiuRoundPo result = null;
    for (MustNiuNiuRoundPo item : list) {
      if (item.getRound() == round) {
        result = item;
        break;
      }
    }
    return result;
  }

  private MustNiuNiuStartOptionPo findStartOptionPo(List<MustNiuNiuStartOptionPo> list,
      int startOptionId) {
    MustNiuNiuStartOptionPo result = null;
    for (MustNiuNiuStartOptionPo item : list) {
      if (item.getId() == startOptionId) {
        result = item;
        break;
      }
    }
    return result;
  }

  private MustNiuNiuExtrBetOptionPo findExtrBetOptionPo(List<MustNiuNiuExtrBetOptionPo> list,
      int extrBetOptionId) {
    MustNiuNiuExtrBetOptionPo result = null;
    for (MustNiuNiuExtrBetOptionPo item : list) {
      if (item.getId() == extrBetOptionId) {
        result = item;
        break;
      }
    }
    return result;
  }

  private MustNiuNiuCardMultiplePo findMultiplePo(List<MustNiuNiuCardMultiplePo> list,
      int multipleId) {
    MustNiuNiuCardMultiplePo result = null;
    for (MustNiuNiuCardMultiplePo item : list) {
      if (item.getId() == multipleId) {
        result = item;
        break;
      }
    }
    return result;
  }

  private List<MustNiuNiuCardSpecialMultiplePo> findSpecialMultiplePo(
      List<MustNiuNiuCardSpecialMultiplePo> list, List<Integer> specialMultipleIds) {
    List<MustNiuNiuCardSpecialMultiplePo> result =
        new ArrayList<MustNiuNiuCardSpecialMultiplePo>();
    for (MustNiuNiuCardSpecialMultiplePo item : list) {
      if (specialMultipleIds.contains(item.getId())) {
        result.add(item);
      }
    }
    return result;
  }

  private List<MustNiuNiuAdvanceOptionPo> findAdvanceOptionPo(
      List<MustNiuNiuAdvanceOptionPo> list, List<Integer> advanceOptionIds) {
    List<MustNiuNiuAdvanceOptionPo> result = new ArrayList<MustNiuNiuAdvanceOptionPo>();
    for (MustNiuNiuAdvanceOptionPo item : list) {
      if (advanceOptionIds.contains(item.getId())) {
        result.add(item);
      }
    }
    return result;
  }

  private MustNiuNiuBankerChipPo findBankerChipPo(List<MustNiuNiuBankerChipPo> list,
      int bankerChipsId) {
    MustNiuNiuBankerChipPo result = null;
    for (MustNiuNiuBankerChipPo item : list) {
      if (item.getId() == bankerChipsId) {
        result = item;
        break;
      }
    }
    return result;
  }


  private MustNiuNiuCallBankerMultiplePo findCallBankerMultiplePo(
      List<MustNiuNiuCallBankerMultiplePo> list, int multiple) {
    MustNiuNiuCallBankerMultiplePo result = null;
    for (MustNiuNiuCallBankerMultiplePo item : list) {
      if (item.getMultiple() == multiple) {
        result = item;
        break;
      }
    }
    return result;
  }

  private RoomCardMustNiuNiuFeeDomain findNiuNiuFeeDomain(int type, int seat, int round,
      int fee) {
    RoomCardMustNiuNiuFeeDomain result = null;
    for (RoomCardMustNiuNiuFeeDomain item : roomCardMustNiuNiuFeeDic.list()) {
      if (item.getType() == (byte) type && item.getSeats() == (byte) seat
          && item.getRounds() == (byte) round) {
        if (item.getPayByAaFee() == fee || item.getPayByOwnerFee() == fee) {
          result = item;
          break;
        }
      }
    }
    return result;
  }

  private boolean isShowCardCallBanker(int type) {
    return type == MustNiuNiuGame.SHOW_CARD_CALL_BANKER.intValue();
  }

  private boolean isNiuNiuBeBanker(int type) {
    return type == MustNiuNiuGame.NIUNIU_BE_BANKER.intValue();
  }

  private boolean isOwnerBeBanker(int type) {
    return type == MustNiuNiuGame.OWNER_BE_BANKER.intValue();
  }

  private boolean isFreeCallBanker(int type) {
    return type == MustNiuNiuGame.FREE_CALL_BANKER.intValue();
  }
}
